月度归档: 2021 年 3 月

ios 用xcode4.2开发 访问web service的功能

1。后台利用 cxf 构建一个web service服务。

  • HelloWorld.java
[java] view plain copy

  1. /**
  2.  * 
  3.  */
  4. package com.alcor.jws.test;
  5. import javax.jws.WebMethod;
  6. import javax.jws.WebService;
  7. import org.apache.cxf.feature.Features;
  8. /**
  9.  * @author 徐泽宇(roamer)
  10.  * 
  11.  *         2010-7-10
  12.  */
  13. @WebService
  14. @Features(features = “org.apache.cxf.feature.LoggingFeature”)
  15. public interface HelloWorld {
  16.     @WebMethod
  17.     String sayHi(String text);
  18.     @WebMethod
  19.     boolean userLogon(String username,String userpasswd);
  20. }
  1. /**
  2. *
  3. */
  4. package com.alcor.jws.test;
  5. import javax.jws.WebMethod;
  6. import javax.jws.WebService;
  7. import org.apache.cxf.feature.Features;
  8. /**
  9. * @author 徐泽宇(roamer)
  10. *
  11. * 2010-7-10
  12. */
  13. @WebService
  14. @Features(features = “org.apache.cxf.feature.LoggingFeature”)
  15. public interface HelloWorld {
  16. @WebMethod
  17. String sayHi(String text);
  18. @WebMethod
  19. boolean userLogon(String username,String userpasswd);
  20. }
  • HelloWorldImpl.java
[java] view plain copy

  1. /**
  2.  * 
  3.  */
  4. package com.alcor.jws.test;
  5. import org.apache.cxf.feature.Features;
  6. import org.apache.log4j.Logger;
  7. import javax.jws.WebMethod;
  8. import javax.jws.WebService;
  9. /**
  10.  * @author 徐泽宇(roamer)
  11.  *
  12.  * 2010-7-10
  13.  */
  14. @WebService
  15. @Features(features = “org.apache.cxf.feature.LoggingFeature”)
  16. public class HelloWorldImpl implements HelloWorld {
  17.     /**
  18.      * Logger for this class
  19.      */
  20.     private static final Logger logger = Logger.getLogger(HelloWorldImpl.class);
  21.         @WebMethod
  22.         public String sayHi(String text) {
  23.         if (logger.isDebugEnabled()) {
  24.             logger.debug(“sayHi(String) – start”); //$NON-NLS-1$ 
  25.         }
  26.         String returnString = “Hello,你好: “ + text;
  27.         if (logger.isDebugEnabled()) {
  28.             logger.debug(“返回内容:”+returnString);
  29.             logger.debug(“sayHi(String) – end”); //$NON-NLS-1$ 
  30.         }
  31.             return returnString;
  32.         }
  33.         @WebMethod
  34.         public boolean userLogon(String username ,String userpasswd)
  35.         {
  36.             logger.debug(“用户名是:”+username+“口令是:”+userpasswd);
  37.             if (username.equalsIgnoreCase(“admin”))
  38.             {
  39.                 return true;
  40.             }else{
  41.                 return false;
  42.             }
  43.         }
  44. }
  1. /**
  2. *
  3. */
  4. package com.alcor.jws.test;
  5. import org.apache.cxf.feature.Features;
  6. import org.apache.log4j.Logger;
  7. import javax.jws.WebMethod;
  8. import javax.jws.WebService;
  9. /**
  10. * @author 徐泽宇(roamer)
  11. *
  12. * 2010-7-10
  13. */
  14. @WebService
  15. @Features(features = “org.apache.cxf.feature.LoggingFeature”)
  16. public class HelloWorldImpl implements HelloWorld {
  17. /**
  18. * Logger for this class
  19. */
  20. private static final Logger logger = Logger.getLogger(HelloWorldImpl.class);
  21. @WebMethod
  22. public String sayHi(String text) {
  23. if (logger.isDebugEnabled()) {
  24. logger.debug(“sayHi(String) – start”); //$NON-NLS-1$
  25. }
  26. String returnString = “Hello,你好: “ + text;
  27. if (logger.isDebugEnabled()) {
  28. logger.debug(“返回内容:”+returnString);
  29. logger.debug(“sayHi(String) – end”); //$NON-NLS-1$
  30. }
  31. return returnString;
  32. }
  33. @WebMethod
  34. public boolean userLogon(String username ,String userpasswd)
  35. {
  36. logger.debug(“用户名是:”+username+“口令是:”+userpasswd);
  37. if (username.equalsIgnoreCase(“admin”))
  38. {
  39. return true;
  40. }else{
  41. return false;
  42. }
  43. }
  44. }
  • java 的web service 访问客户端
[cpp] view plain copy

  1. /**
  2.  * 
  3.  */
  4. package com.alcor.jws.test;
  5. import org.apache.cxf.interceptor.LoggingInInterceptor;
  6. import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
  7. /**
  8.  * @author 徐泽宇(roamer)
  9.  * 
  10.  *         2010-7-10
  11.  */
  12. public class Client {
  13.     private Client() {
  14.     }
  15.     public static void main(String args[]) throws Exception {
  16.         /**种方法,通过配置文件来实现  begin
  17.         ApplicationContext ctx = new ClassPathXmlApplicationContext(    “META-INF/WebServiceClient.xml”);
  18.         
  19.         HelloWorld client = (HelloWorld) ctx.getBean(“client”);
  20.         String result = client.sayHi(“Roamer”);
  21.         System.out.println(result);
  22.         
  23.         boolean logonResult = client.userLogon(“roamer”, “passwd”);
  24.         System.out.println(logonResult);
  25.         
  26.         logonResult = client.userLogon(“admin”, “passwd”);
  27.         System.out.println(logonResult);
  28.         */
  29.         JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
  30.         factory.setAddress(“http://localhost:8080/SampleWebService/webservice/HelloWorld”);
  31.         factory.setServiceClass(HelloWorld.class);
  32.         factory.getInInterceptors().add(new LoggingInInterceptor());
  33.         HelloWorld helloWorld = (HelloWorld) factory.create();
  34.         boolean msg = helloWorld.userLogon(“admin”,“World”);
  35.         System.out.println(msg);
  36.     }
  37. }
  1. /**
  2. *
  3. */
  4. package com.alcor.jws.test;
  5. import org.apache.cxf.interceptor.LoggingInInterceptor;
  6. import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
  7. /**
  8. * @author 徐泽宇(roamer)
  9. *
  10. * 2010-7-10
  11. */
  12. public class Client {
  13. private Client() {
  14. }
  15. public static void main(String args[]) throws Exception {
  16. /**种方法,通过配置文件来实现 begin
  17. ApplicationContext ctx = new ClassPathXmlApplicationContext( “META-INF/WebServiceClient.xml”);
  18. HelloWorld client = (HelloWorld) ctx.getBean(“client”);
  19. String result = client.sayHi(“Roamer”);
  20. System.out.println(result);
  21. boolean logonResult = client.userLogon(“roamer”, “passwd”);
  22. System.out.println(logonResult);
  23. logonResult = client.userLogon(“admin”, “passwd”);
  24. System.out.println(logonResult);
  25. */
  26. JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
  27. factory.setAddress(“http://localhost:8080/SampleWebService/webservice/HelloWorld”);
  28. factory.setServiceClass(HelloWorld.class);
  29. factory.getInInterceptors().add(new LoggingInInterceptor());
  30. HelloWorld helloWorld = (HelloWorld) factory.create();
  31. boolean msg = helloWorld.userLogon(“admin”,“World”);
  32. System.out.println(msg);
  33. }
  34. }

2。iphone 客户端的编程

  • LogonViewController.h
[cpp] view plain copy

  1. // 
  2. //  LogonViewController.h 
  3. //  IManager 
  4. // 
  5. //  Created by remote roamer on 11-11-22. 
  6. //  Copyright (c) 2011年 __MyCompanyName__. All rights reserved. 
  7. // 
  8. #import <UIKit/UIKit.h> 
  9. @interface LogonViewController : UIViewController<NSXMLParserDelegate>
  10. {
  11.     IBOutlet UITextField * userNameTextField;
  12.     IBOutlet UITextField * userPasswordTextField;
  13.     IBOutlet UIButton    * userLogonButton;
  14.     IBOutlet UITextField * webServiceURL;
  15.     NSXMLParser *xmlParser;
  16.     BOOL logonResult;
  17.     NSMutableString *soapResults;
  18. }
  19. @property(nonatomic,retain) IBOutlet UITextField * userNameTextField;
  20. @property(nonatomic,retain) IBOutlet UITextField * userPasswordTextField;
  21. @property(nonatomic,retain) IBOutlet UIButton    * userLogonButton;
  22. @property(nonatomic,retain) IBOutlet UITextField * webServiceURL;
  23. @property(nonatomic, retain) NSXMLParser *xmlParser;
  24. @property(nonatomic,retain) NSMutableString * soapResults;
  25. -(IBAction) logonButtonClick:(id)sender;
  26. @end
  1. //
  2. // LogonViewController.h
  3. // IManager
  4. //
  5. // Created by remote roamer on 11-11-22.
  6. // Copyright (c) 2011年 __MyCompanyName__. All rights reserved.
  7. //
  8. #import <UIKit/UIKit.h>
  9. @interface LogonViewController : UIViewController<NSXMLParserDelegate>
  10. {
  11. IBOutlet UITextField * userNameTextField;
  12. IBOutlet UITextField * userPasswordTextField;
  13. IBOutlet UIButton * userLogonButton;
  14. IBOutlet UITextField * webServiceURL;
  15. NSXMLParser *xmlParser;
  16. BOOL logonResult;
  17. NSMutableString *soapResults;
  18. }
  19. @property(nonatomic,retain) IBOutlet UITextField * userNameTextField;
  20. @property(nonatomic,retain) IBOutlet UITextField * userPasswordTextField;
  21. @property(nonatomic,retain) IBOutlet UIButton * userLogonButton;
  22. @property(nonatomic,retain) IBOutlet UITextField * webServiceURL;
  23. @property(nonatomic, retain) NSXMLParser *xmlParser;
  24. @property(nonatomic,retain) NSMutableString * soapResults;
  25. -(IBAction) logonButtonClick:(id)sender;
  26. @end
  • LogonViewController.m
    [cpp] view plain copy

    1. // 
    2. //  LogonViewController.m 
    3. //  IManager 
    4. // 
    5. //  Created by remote roamer on 11-11-22. 
    6. //  Copyright (c) 2011年 __MyCompanyName__. All rights reserved. 
    7. // 
    8. #import “LogonViewController.h” 
    9. @implementation LogonViewController
    10. @synthesize userNameTextField;
    11. @synthesize userPasswordTextField;
    12. @synthesize userLogonButton;
    13. @synthesize webServiceURL;
    14. @synthesize xmlParser;
    15. @synthesize soapResults;
    16. -(IBAction) logonButtonClick:(id)sender
    17. {
    18.     logonResult = false;
    19.     NSString *soapMessage = [NSString stringWithFormat:
    20.                              @“<?xml version=\”1.0\” encoding=\”utf-8\”?>\n”
    21.                              “<soap:Envelope xmlns:xsi=\”http://www.w3.org/2001/XMLSchema-instance\” xmlns:xsd=\”http://www.w3.org/2001/XMLSchema\” xmlns:soap=\”http://schemas.xmlsoap.org/soap/envelope/\”>”
    22.                              “<soap:Body>\n”
    23.                              “<ns1:userLogon xmlns:ns1=\”http://localhost:8080/SampleWebService/webservice/HelloWorld/\”>”
    24.                              “<arg0>%@</arg0>”
    25.                              “<arg1>%@</arg1>”
    26.                              “</ns1:userLogon>”
    27.                              “</soap:Body>\n”
    28.                              “</soap:Envelope>”,self.userNameTextField.text,self.userPasswordTextField.text];
    29.     NSLog(@“调用webserivce的字符串是:%@”,soapMessage);
    30.     //请求发送到的路径 
    31.     NSString *msgLength = [NSString stringWithFormat:@“%d”, [soapMessage length]];
    32.     NSURL *url = [NSURL URLWithString:@“http://localhost:8080/SampleWebService/webservice/HelloWorld/”];
    33.     NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url];
    34.      //以下对请求信息添加属性前四句是必有的, 
    35.     [urlRequest addValue: @“text/xml; charset=utf-8” forHTTPHeaderField:@“Content-Type”];
    36.     [urlRequest addValue: @“http://localhost:8080/SampleWebService/webservice/HelloWorld” forHTTPHeaderField:@“SOAPAction”];
    37.     [urlRequest addValue: msgLength forHTTPHeaderField:@“Content-Length”];
    38.     [urlRequest setHTTPMethod:@“POST”];
    39.     [urlRequest setHTTPBody: [soapMessage dataUsingEncoding:NSUTF8StringEncoding]];
    40.     //请求 
    41.     NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
    42.     theConnection = nil;
    43. }
    44. //如果调用有错误,则出现此信息 
    45. -(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
    46. {
    47.     NSLog(@“ERROR with theConenction”);
    48.     UIAlertView * alert =
    49.         [[UIAlertView alloc]
    50.              initWithTitle:@“提示”
    51.              message:[error description]
    52.              delegate:self
    53.              cancelButtonTitle:nil
    54.              otherButtonTitles:@“OK”, nil];
    55.     [alert show];
    56. }
    57. //调用成功,获得soap信息 
    58. -(void) connection:(NSURLConnection *) connection didReceiveData:(NSData *)responseData
    59. {
    60.     NSString * returnSoapXML = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
    61.     NSLog(@“返回的soap信息是:%@”,returnSoapXML);
    62.     //开始解析xml 
    63.     xmlParser = [[NSXMLParser alloc] initWithData: responseData];
    64.     [xmlParser setDelegate:self];
    65.     [xmlParser setShouldResolveExternalEntities: YES];
    66.     [xmlParser parse];
    67.     if(logonResult){
    68.         UIAlertView * alert = [[UIAlertView alloc] initWithTitle:@“登录成功” message:returnSoapXML delegate:nil cancelButtonTitle:@“ok” otherButtonTitles: nil];
    69.         [alert show];
    70.     }
    71. }
    72. //如果soap的返回字符串比较多。需要实现以下这个方法,配合 didReceiveData 方法来正确的接受到所有的soap字符串 
    73. //原因是:如果soap的返回字符串比较多。didReceiveData 这个方法会多次被调用。如果把soap解析的功能直接写在didReceiveData这个方法里面。会出现错误。这个时候,就需要 和connectionDidFinishLoading 联用。实现思路是:定义一个类变量NSMutableString * returnSoapXML;用于存放返回的soap字符串。 
    74. //一旦有返回内容,获得soap信息,追加到结果字符串中 
    75. //-(void) connection:(NSURLConnection *) connection didReceiveData:(NSData *)responseData 
    76. //{ 
    77. //    [returnSoapXML appendString:[[NSMutableString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]]; 
    78. //} 
    79. //*后在connectionDidFinishLoading 方法中实现,对完整的soap字符串的业务处理 
    80. //数据全部接受成功以后调用 
    81. /*
    82. – (void)connectionDidFinishLoading:(NSURLConnection *)connection
    83. {
    84.     NSLog(@”从远程ws中调用获得客户简单信息的调用成功!”);    
    85.     NSLog(@”返回的soap信息是:%@”,returnSoapXML);
    86.     //从soap 信息中解析出CusotmerInfo对象数组,并且保存到数据库中
    87.     NSLog(@”开始保存ws返回的内容到本地数据库”);
    88.     [[[SoapRtnJsonParser alloc] init] parse2CustomersInfo:[returnSoapXML dataUsingEncoding:NSUTF8StringEncoding]];
    89. }
    90. */
    91. -(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
    92. {
    93.     NSLog(@“返回的soap内容中,return值是: %@”,string);
    94.     if ([string isEqualToString:@“true”])
    95.     {
    96.         logonResult = YES;
    97.     }else{
    98.         logonResult = NO;
    99.     }
    100. }
    101. – (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
    102. {
    103.     self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    104.     if (self) {
    105.         // Custom initialization 
    106.     }
    107.     return self;
    108. }
    109. – (void)didReceiveMemoryWarning
    110. {
    111.     // Releases the view if it doesn’t have a superview. 
    112.     [super didReceiveMemoryWarning];
    113.     // Release any cached data, images, etc that aren’t in use. 
    114. }
    115. #pragma mark – View lifecycle 
    116. – (void)viewDidLoad
    117. {
    118.     [super viewDidLoad];
    119.     // Do any additional setup after loading the view from its nib. 
    120. }
    121. – (void)viewDidUnload
    122. {
    123.     [super viewDidUnload];
    124.     // Release any retained subviews of the main view. 
    125.     // e.g. self.myOutlet = nil; 
    126. }
    127. – (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
    128. {
    129.     // Return YES for supported orientations 
    130.     return (interfaceOrientation == UIInterfaceOrientationPortrait);
    131. }
    132. @end
    1. //
    2. // LogonViewController.m
    3. // IManager
    4. //
    5. // Created by remote roamer on 11-11-22.
    6. // Copyright (c) 2011年 __MyCompanyName__. All rights reserved.
    7. //
    8. #import “LogonViewController.h”
    9. @implementation LogonViewController
    10. @synthesize userNameTextField;
    11. @synthesize userPasswordTextField;
    12. @synthesize userLogonButton;
    13. @synthesize webServiceURL;
    14. @synthesize xmlParser;
    15. @synthesize soapResults;
    16. -(IBAction) logonButtonClick:(id)sender
    17. {
    18. logonResult = false;
    19. NSString *soapMessage = [NSString stringWithFormat:
    20. @“<?xml version=\”1.0\” encoding=\”utf-8\”?>\n”
    21. “<soap:Envelope xmlns:xsi=\”http://www.w3.org/2001/XMLSchema-instance\” xmlns:xsd=\”http://www.w3.org/2001/XMLSchema\” xmlns:soap=\”http://schemas.xmlsoap.org/soap/envelope/\”>”
    22. “<soap:Body>\n”
    23. “<ns1:userLogon xmlns:ns1=\”http://localhost:8080/SampleWebService/webservice/HelloWorld/\”>”
    24. “<arg0>%@</arg0>”
    25. “<arg1>%@</arg1>”
    26. “</ns1:userLogon>”
    27. “</soap:Body>\n”
    28. “</soap:Envelope>”,self.userNameTextField.text,self.userPasswordTextField.text];
    29. NSLog(@“调用webserivce的字符串是:%@”,soapMessage);
    30. //请求发送到的路径
    31. NSString *msgLength = [NSString stringWithFormat:@“%d”, [soapMessage length]];
    32. NSURL *url = [NSURL URLWithString:@“http://localhost:8080/SampleWebService/webservice/HelloWorld/”];
    33. NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url];
    34. //以下对请求信息添加属性前四句是必有的,
    35. [urlRequest addValue: @“text/xml; charset=utf-8” forHTTPHeaderField:@“Content-Type”];
    36. [urlRequest addValue: @“http://localhost:8080/SampleWebService/webservice/HelloWorld” forHTTPHeaderField:@“SOAPAction”];
    37. [urlRequest addValue: msgLength forHTTPHeaderField:@“Content-Length”];
    38. [urlRequest setHTTPMethod:@“POST”];
    39. [urlRequest setHTTPBody: [soapMessage dataUsingEncoding:NSUTF8StringEncoding]];
    40. //请求
    41. NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
    42. theConnection = nil;
    43. }
    44. //如果调用有错误,则出现此信息
    45. -(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
    46. {
    47. NSLog(@“ERROR with theConenction”);
    48. UIAlertView * alert =
    49. [[UIAlertView alloc]
    50. initWithTitle:@“提示”
    51. message:[error description]
    52. delegate:self
    53. cancelButtonTitle:nil
    54. otherButtonTitles:@“OK”, nil];
    55. [alert show];
    56. }
    57. //调用成功,获得soap信息
    58. -(void) connection:(NSURLConnection *) connection didReceiveData:(NSData *)responseData
    59. {
    60. NSString * returnSoapXML = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
    61. NSLog(@“返回的soap信息是:%@”,returnSoapXML);
    62. //开始解析xml
    63. xmlParser = [[NSXMLParser alloc] initWithData: responseData];
    64. [xmlParser setDelegate:self];
    65. [xmlParser setShouldResolveExternalEntities: YES];
    66. [xmlParser parse];
    67. if(logonResult){
    68. UIAlertView * alert = [[UIAlertView alloc] initWithTitle:@“登录成功” message:returnSoapXML delegate:nil cancelButtonTitle:@“ok” otherButtonTitles: nil];
    69. [alert show];
    70. }
    71. }
    72. //如果soap的返回字符串比较多。需要实现以下这个方法,配合 didReceiveData 方法来正确的接受到所有的soap字符串
    73. //原因是:如果soap的返回字符串比较多。didReceiveData 这个方法会多次被调用。如果把soap解析的功能直接写在didReceiveData这个方法里面。会出现错误。这个时候,就需要 和connectionDidFinishLoading 联用。实现思路是:定义一个类变量NSMutableString * returnSoapXML;用于存放返回的soap字符串。
    74. //一旦有返回内容,获得soap信息,追加到结果字符串中
    75. //-(void) connection:(NSURLConnection *) connection didReceiveData:(NSData *)responseData
    76. //{
    77. // [returnSoapXML appendString:[[NSMutableString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]];
    78. //}
    79. //*后在connectionDidFinishLoading 方法中实现,对完整的soap字符串的业务处理
    80. //数据全部接受成功以后调用
    81. /*
    82. – (void)connectionDidFinishLoading:(NSURLConnection *)connection
    83. {
    84. NSLog(@”从远程ws中调用获得客户简单信息的调用成功!”);
    85. NSLog(@”返回的soap信息是:%@”,returnSoapXML);
    86. //从soap 信息中解析出CusotmerInfo对象数组,并且保存到数据库中
    87. NSLog(@”开始保存ws返回的内容到本地数据库”);
    88. [[[SoapRtnJsonParser alloc] init] parse2CustomersInfo:[returnSoapXML dataUsingEncoding:NSUTF8StringEncoding]];
    89. }
    90. */
    91. -(void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string
    92. {
    93. NSLog(@“返回的soap内容中,return值是: %@”,string);
    94. if ([string isEqualToString:@“true”])
    95. {
    96. logonResult = YES;
    97. }else{
    98. logonResult = NO;
    99. }
    100. }
    101. – (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
    102. {
    103. self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
    104. if (self) {
    105. // Custom initialization
    106. }
    107. return self;
    108. }
    109. – (void)didReceiveMemoryWarning
    110. {
    111. // Releases the view if it doesn’t have a superview.
    112. [super didReceiveMemoryWarning];
    113. // Release any cached data, images, etc that aren’t in use.
    114. }
    115. #pragma mark – View lifecycle
    116. – (void)viewDidLoad
    117. {
    118. [super viewDidLoad];
    119. // Do any additional setup after loading the view from its nib.
    120. }
    121. – (void)viewDidUnload
    122. {
    123. [super viewDidUnload];
    124. // Release any retained subviews of the main view.
    125. // e.g. self.myOutlet = nil;
    126. }
    127. – (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
    128. {
    129. // Return YES for supported orientations
    130. return (interfaceOrientation == UIInterfaceOrientationPortrait);
    131. }
    132. @end

其中要注意的几点。

  • [cpp] view plain copy

    1. <ns1:userLogon xmlns:ns1=\“http://localhost:8080/SampleWebService/webservice/HelloWorld/\”>”
    2.                              “<arg0>%@</arg0>”
    3.                              “<arg1>%@</arg1>”
    4.                              “</ns1:userLogon>”
    1. <ns1:userLogon xmlns:ns1=\“http://localhost:8080/SampleWebService/webservice/HelloWorld/\”>”
    2. “<arg0>%@</arg0>”
    3. “<arg1>%@</arg1>”
    4. “</ns1:userLogon>”

    中web service 中的 userLogon方法的调用 要用ns1来引用。

  • 传递的参数 不能和 webservice的变量名来写。而只能写成 arg0 和 arg1 这种方式。我查阅其他网上资料,都是写成<username>和<userpasswd>这种element的形式。但是在我的这个演示中,如果写成这种方式。后台会无法获得传入的变量。而用arg0 这种方式是可以传入。我不清楚是否是和 java cxf的webservice搭建环境和版本有关。

注意:如果后台的WebService是.net开发的,调用的程序略有不同:

例如 后台的ws服务链接是 :http://10.100.111.231:9000/MobileService.asmx

那么访问这个url,会返回如下内容:

这个页面提示了SOAP 1.1 和 SOAP 1.2 的调用的内容

[html] view plain copy

  1. SOAP 1.1
  2. The following is a sample SOAP 1.1 request and response. The placeholders shown need to be replaced with actual values.
  3.  POST /MobileService.asmx HTTP/1.1
  4. Host: 10.100.111.231
  5. Content-Type: text/xml; charset=utf-8
  6. Content-Length: length
  7. SOAPAction: “http://tempuri.org/Logon”
  8. <?xml version=“1.0” encoding=“utf-8”?>
  9. <soap:Envelope xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance” xmlns:xsd=“http://www.w3.org/2001/XMLSchema” xmlns:soap=“http://schemas.xmlsoap.org/soap/envelope/”>
  10.   <soap:Body>
  11.     <Logon xmlns=“http://tempuri.org/”>
  12.       <userName>string</userName>
  13.       <password>string</password>
  14.     </Logon>
  15.   </soap:Body>
  16. </soap:Envelope>
  17. HTTP/1.1 200 OK
  18. Content-Type: text/xml; charset=utf-8
  19. Content-Length: length
  20. <?xml version=“1.0” encoding=“utf-8”?>
  21. <soap:Envelope xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance” xmlns:xsd=“http://www.w3.org/2001/XMLSchema” xmlns:soap=“http://schemas.xmlsoap.org/soap/envelope/”>
  22.   <soap:Body>
  23.     <LogonResponse xmlns=“http://tempuri.org/”>
  24.       <LogonResult>string</LogonResult>
  25.     </LogonResponse>
  26.   </soap:Body>
  27. </soap:Envelope>
  28. SOAP 1.2
  29. The following is a sample SOAP 1.2 request and response. The placeholders shown need to be replaced with actual values.
  30. POST /MobileService.asmx HTTP/1.1
  31. Host: 10.100.111.231
  32. Content-Type: application/soap+xml; charset=utf-8
  33. Content-Length: length
  34. <?xml version=“1.0” encoding=“utf-8”?>
  35. <soap12:Envelope xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance” xmlns:xsd=“http://www.w3.org/2001/XMLSchema” xmlns:soap12=“http://www.w3.org/2003/05/soap-envelope”>
  36.   <soap12:Body>
  37.     <Logon xmlns=“http://tempuri.org/”>
  38.       <userName>string</userName>
  39.       <password>string</password>
  40.     </Logon>
  41.   </soap12:Body>
  42. </soap12:Envelope>
  43. HTTP/1.1 200 OK
  44. Content-Type: application/soap+xml; charset=utf-8
  45. Content-Length: length
  46. <?xml version=“1.0” encoding=“utf-8”?>
  47. <soap12:Envelope xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance” xmlns:xsd=“http://www.w3.org/2001/XMLSchema” xmlns:soap12=“http://www.w3.org/2003/05/soap-envelope”>
  48.   <soap12:Body>
  49.     <LogonResponse xmlns=“http://tempuri.org/”>
  50.       <LogonResult>string</LogonResult>
  51.     </LogonResponse>
  52.   </soap12:Body>
  53. </soap12:Envelope>
  1. SOAP 1.1
  2. The following is a sample SOAP 1.1 request and response. The placeholders shown need to be replaced with actual values.
  3.  POST /MobileService.asmx HTTP/1.1
  4. Host: 10.100.111.231
  5. Content-Type: text/xml; charset=utf-8
  6. Content-Length: length
  7. SOAPAction: “http://tempuri.org/Logon”
  8. <?xml version=”1.0″ encoding=”utf-8″?>
  9. <soap:Envelope xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance” xmlns:xsd=“http://www.w3.org/2001/XMLSchema” xmlns:soap=“http://schemas.xmlsoap.org/soap/envelope/”>
  10. <soap:Body>
  11. <Logon xmlns=“http://tempuri.org/”>
  12. <userName>string</userName>
  13. <password>string</password>
  14. </Logon>
  15. </soap:Body>
  16. </soap:Envelope>
  17. HTTP/1.1 200 OK
  18. Content-Type: text/xml; charset=utf-8
  19. Content-Length: length
  20. <?xml version=”1.0″ encoding=”utf-8″?>
  21. <soap:Envelope xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance” xmlns:xsd=“http://www.w3.org/2001/XMLSchema” xmlns:soap=“http://schemas.xmlsoap.org/soap/envelope/”>
  22. <soap:Body>
  23. <LogonResponse xmlns=“http://tempuri.org/”>
  24. <LogonResult>string</LogonResult>
  25. </LogonResponse>
  26. </soap:Body>
  27. </soap:Envelope>
  28. SOAP 1.2
  29. The following is a sample SOAP 1.2 request and response. The placeholders shown need to be replaced with actual values.
  30. POST /MobileService.asmx HTTP/1.1
  31. Host: 10.100.111.231
  32. Content-Type: application/soap+xml; charset=utf-8
  33. Content-Length: length
  34. <?xml version=”1.0″ encoding=”utf-8″?>
  35. <soap12:Envelope xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance” xmlns:xsd=“http://www.w3.org/2001/XMLSchema” xmlns:soap12=“http://www.w3.org/2003/05/soap-envelope”>
  36. <soap12:Body>
  37. <Logon xmlns=“http://tempuri.org/”>
  38. <userName>string</userName>
  39. <password>string</password>
  40. </Logon>
  41. </soap12:Body>
  42. </soap12:Envelope>
  43. HTTP/1.1 200 OK
  44. Content-Type: application/soap+xml; charset=utf-8
  45. Content-Length: length
  46. <?xml version=”1.0″ encoding=”utf-8″?>
  47. <soap12:Envelope xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance” xmlns:xsd=“http://www.w3.org/2001/XMLSchema” xmlns:soap12=“http://www.w3.org/2003/05/soap-envelope”>
  48. <soap12:Body>
  49. <LogonResponse xmlns=“http://tempuri.org/”>
  50. <LogonResult>string</LogonResult>
  51. </LogonResponse>
  52. </soap12:Body>
  53. </soap12:Envelope>

 

那么,我们在objectiveC中代码应该写成

static NSString * wsURL = @”http://10.100.111.231:9000/MobileService.asmx”;

NSString *soapMessage = [NSString stringWithFormat:
@”<?xml version=\”1.0\” encoding=\”utf-8\”?>\n”
“<soap:Envelope xmlns:xsi=\”http://www.w3.org/2001/XMLSchema-instance\” xmlns:xsd=\”http://www.w3.org/2001/XMLSchema\” xmlns:soap=\”http://schemas.xmlsoap.org/soap/envelope/\”>”
“<soap:Body>\n”
“<Logon xmlns=\”http://tempuri.org/\”>”
“<userName>%@</userName>”
“<password>%@</password>”
“</Logon>”
“</soap:Body>\n”
“</soap:Envelope>”,self.userNameTextField.text,self.userPasswordTextField.text];

NSLog(@”调用webserivce的字符串是:%@”,soapMessage);
//请求发送到的路径
NSString *msgLength = [NSString stringWithFormat:@”%d”, [soapMessage length]];
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@”%@”,wsURL]];
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url];

//以下对请求信息添加属性前四句是必有的,
[urlRequest addValue: @”text/xml; charset=utf-8″ forHTTPHeaderField:@”Content-Type”];
[urlRequest addValue:@”http://tempuri.org/Logon” forHTTPHeaderField:@”SOAPAction”];
NSLog(@”SOAPAction is %@ “,@”http://tempuri.org/Logon”);
[urlRequest addValue: msgLength forHTTPHeaderField:@”Content-Length”];
[urlRequest setHTTPMethod:@”POST”];
[urlRequest setHTTPBody: [soapMessage dataUsingEncoding:NSUTF8StringEncoding]];

//请求
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:urlRequest delegate:self];
theConnection = nil;

注意红色的代码,这个http://tempurl.org/Logon 是要和 .net中的代码一致。否则无法访问。

这个字符串的内容可以从asmx返回帮助界面来获得。

IOS学习之ios开发之数据的持久化存储机制

IOS中数据的持久化保存这块内容,类似于Android中文件的几种常见的存储方式。
对于数据的持久化存储,ios中一般提供了4种不同的机制。
1.属性列表
2.对象归档
3.数据库存储(SQLite3)
4.苹果公司提供的持久性工具Core Data。

其实储存的形式无非就这么几种,而我们还必须要关心的是,这些文件会被放置在那个文件下,然后如何读取。
也就是说:IOS上数据存储,我们要了解的两点,数据存储格式(也就是存储机制),数据存储位置。
1》文件如何存储(如上面4点)
2》文件存储在哪里。
对于数据的操作,其实我们关心的是操作的速率。
就好比在Adnroid中偏好存储,数据库存储,io存储一样。
我大致问了我们公司新来的ios哥们,他说他们培训机构基本对数据操作这块就讲了属性列表和数据库,以及普通的文件存储(比如音视频图这些多媒体数据)。
我就只好先看看书了。

一:应用文件目录
首先我们来看了解下ios数据存储位置,因为只有知道位置路径我们才能去读取数据,而数据的持久化机制不过是针对操作速率来考虑的,
比如我们大致知道属性列表(既键值对形式)的存储熟虑应该高于数据库高于io文件流存储。
我们在选择用何种机制存储数据,主要也是看数据的形式。

一个ios应用安装后大致会有如下文件夹及其对应路径:

%title插图%num

在mac上看模拟器中应用路径:
/Users/nono/Library/Application Support/iPhone Simulator/5.1/Applications/2D135859-1E80-4754-B36D-34A53C521DE3

你在finder中的home下可能找不到Library这个目录,因为貌似是影藏起来了(我这机器上是,在终端可以看到)。
*后那一窜的类似序列号的东西就是ios自动给应用生成的一组应用唯一识别码*为了应用的home目录名。
其下面就是上图所示了。
书上对这些文件夹介绍:

Document:应用程序将其数据存储在这个文件夹下,基于NSUserDefaults的首选项的设置除外。

简单理解是,基本上我们要操作的一些数据都是存储在这个文件夹下面的

TIPS:这边提下一点,对于ios系统这么分配文件夹,是因为在设备进行同步时,ITunes有选择性的意识来备份文件。

比如我们可以猜到,tmp下的应该就不会备份了。

对于Document文件夹目录路径的获取,API提供了这么一种方法:

[cpp] view plain copy

  1. NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  2.    NSString *docPath = [paths objectAtIndex:0];
  1. NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  2. NSString *docPath = [paths objectAtIndex:0];

Library:基于NSUserDefault首选项设置存储在其下Preferences文件夹中,简单来说,这个文件夹一般你很少操作到。

书上对于这部分基本没介绍。估计对于初级部分是跳过了。

Tmp:应用临时存储文件,当不需要时,应用负责删除其下的文件数据。

该文件也提供了目录获取方法:

[cpp] view plain copy

  1. NSString *tmpDoc = NSTemporaryDirectory();
NSString *tmpDoc = NSTemporaryDirectory();

应用程序文件:这个基本没提到书上,但是我们大致可以猜测,这就是整个应用程序的程序文件夹吧。

好了,以上我们大致解决了我们提到的*个点,文件存储目录

二:数据存储机制

1.属性列表

这个其实我们早见过,plist就是,感觉用来存储键值对小数据是*合适,因为速率很高。

这个存储机制很简单,对于前面我们使用过了在plist文件来读取数据填充一些列表,只不过那会plist文件存储位置不同,

用的是Mainbundle什么的来返回文件夹,其实这边我也推测,上面提到有个应用程序文件夹,它下面的文件就是这么来读取的~(反正暂时不管他)

这边不过就是改变了存储位置,数据操作还是一样的

[cpp] view plain copy

  1. NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  2. NSString *docPath = [paths objectAtIndex:0];
  3. NSString *myFile = [docPath stringByAppendingPathComponent:@“my.list”];
  4. //读取文件 
  5. NSArray *array = [[NSArray alloc] initWithContentsOfFile:myFile];
  6. //操作完若修改了数据则,写入文件 
  7. [array writeToFile:myFile atomically:YES];
  1. NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  2. NSString *docPath = [paths objectAtIndex:0];
  3. NSString *myFile = [docPath stringByAppendingPathComponent:@“my.list”];
  4. //读取文件
  5. NSArray *array = [[NSArray alloc] initWithContentsOfFile:myFile];
  6. //操作完若修改了数据则,写入文件
  7. [array writeToFile:myFile atomically:YES];

2.对象归档

上面的属性列表存储机制,我们都知道,这个机制支持NSArray,NSDictionary,NSData,NSString,NSNumber,NSDate 等等

这些对象直接写入plist文件中。

那么对于一些复杂对象,我要保存整个这个对象数据呢?

反正我是这么觉得,这个机制很像java中的对象整体序列化。当然,这些数据在读取是就需要遵循一种墨守成规的协议了。

首先我们定义的对象类,必须实现NSCoding和NSCopying协议(额,网上说后面这个不实现也可以,我猜是他对象没有copy操作,因此没出错)书本上反正是实现了这两个协议
然后归档中用到的操作类
NSKeyedArchiver
这边我们定义一个对象,h文件中定义两属性,申明要实现的NSCoding和NSCopying协议
实现文件

[cpp] view plain copy

  1. // 
  2. //  TestObj.m 
  3. //  DataStorageTest 
  4. // 
  5. //  Created by Nono on 12-5-12. 
  6. //  Copyright (c) 2012年 NonoWithLilith. All rights reserved. 
  7. // 
  8. #import “TestObj.h” 
  9. @implementation TestObj
  10. @synthesize stringA = stringA;
  11. @synthesize stringB = stringB;
  12. #pragma mark – 
  13. #pragma NSCoding协议实现实现 
  14. – (void)encodeWithCoder:(NSCoder *)aCoder
  15. {   //encoder 
  16.     [aCoder encodeObject:stringAforKey:@“1”];
  17.     [aCoder encodeObject:stringBforKey:@“2”];
  18. }
  19. – (id)initWithCoder:(NSCoder *)aDecoder
  20. {
  21.     //decoder 
  22.     if (self = [superinit]) {
  23.         stringA = [[aDecoder decodeObjectForKey:@“1”] retain];
  24.         stringB = [[aDecoder decodeObjectForKey:@“2”] retain];
  25.     }
  26.     returnself;
  27. }
  28. #pragma NSCopying协议实现 
  29. – (id)copyWithZone:(NSZone *)zone
  30. {
  31.     TestObj *copy = [[[selfclass] allocWithZone:zone] init];
  32.     copy.stringA = [[self.stringAcopyWithZone:zone] autorelease];
  33.     copy.stringB = [[self.stringBcopyWithZone:zone] autorelease];
  34.     return copy;
  35. }
  36. @end
  1. //
  2. // TestObj.m
  3. // DataStorageTest
  4. //
  5. // Created by Nono on 12-5-12.
  6. // Copyright (c) 2012年 NonoWithLilith. All rights reserved.
  7. //
  8. #import “TestObj.h”
  9. @implementation TestObj
  10. @synthesize stringA = stringA;
  11. @synthesize stringB = stringB;
  12. #pragma mark –
  13. #pragma NSCoding协议实现实现
  14. – (void)encodeWithCoder:(NSCoder *)aCoder
  15. { //encoder
  16. [aCoder encodeObject:stringAforKey:@“1”];
  17. [aCoder encodeObject:stringBforKey:@“2”];
  18. }
  19. – (id)initWithCoder:(NSCoder *)aDecoder
  20. {
  21. //decoder
  22. if (self = [superinit]) {
  23. stringA = [[aDecoder decodeObjectForKey:@“1”] retain];
  24. stringB = [[aDecoder decodeObjectForKey:@“2”] retain];
  25. }
  26. returnself;
  27. }
  28. #pragma NSCopying协议实现
  29. – (id)copyWithZone:(NSZone *)zone
  30. {
  31. TestObj *copy = [[[selfclass] allocWithZone:zone] init];
  32. copy.stringA = [[self.stringAcopyWithZone:zone] autorelease];
  33. copy.stringB = [[self.stringBcopyWithZone:zone] autorelease];
  34. return copy;
  35. }
  36. @end

然后是对对象归档的读取和写入

[cpp] view plain copy

  1. //读取归档文件 
  2.    NSData *data = [[NSMutableDataalloc] initWithContentsOfFile:myFile];
  3.    NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiveralloc] initForReadingWithData:data];
  4.    TestObj * test = [unarchiver decodeObjectForKey:@“data”];
  5.    [unarchiver finishDecoding];
  6.    [data release];
  7.    [unarchiver release];
  8.    //写入归档文件 
  9.    NSMutableData *data1 = [[NSMutableDataalloc] init];
  10.    NSKeyedArchiver *archiver = [[NSKeyedArchiveralloc] initForWritingWithMutableData:data1];
  11.    [archiver encodeObject:test forKey:@“data”];
  12.    [archiver finishEncoding];
  13.    [data writeToFile:myFile atomically:YES];
  14.    [data1 release];
  15.    [archiver release];
  16.    [test release];
  1. //读取归档文件
  2. NSData *data = [[NSMutableDataalloc] initWithContentsOfFile:myFile];
  3. NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiveralloc] initForReadingWithData:data];
  4. TestObj * test = [unarchiver decodeObjectForKey:@“data”];
  5. [unarchiver finishDecoding];
  6. [data release];
  7. [unarchiver release];
  8. //写入归档文件
  9. NSMutableData *data1 = [[NSMutableDataalloc] init];
  10. NSKeyedArchiver *archiver = [[NSKeyedArchiveralloc] initForWritingWithMutableData:data1];
  11. [archiver encodeObject:test forKey:@“data”];
  12. [archiver finishEncoding];
  13. [data writeToFile:myFile atomically:YES];
  14. [data1 release];
  15. [archiver release];
  16. [test release];

但是问了下新同事,据说这个用到也是蛮少,至少他目前。
但是,我看了下,觉得这个和Android 中Parcelable
太尼玛像似了

三.数据库存储
和Android一样,ios中也是用了SQLite3这种嵌入式数据库。

这个网上例子是很多了。我这边就大致看下了数据库的打开,

数据库表创建,查询,插入

[cpp] view plain copy

  1.  //数据库操作 
  2.  sqlite3 *database;
  3. // const NSString * dbname = @”mydb” 
  4.  int result;
  5.  //打开一个指定路径的现有的数据库,如果没有则会新建一个db库 
  6.  result =  sqlite3_open([myFile UTF8String], &database);
  7.  if (result != SQLITE_OK) {
  8.      sqlite3_close(database);
  9.  }
  10.  //创建一个db表 
  11.  char *errorMsg;
  12.  NSString *sql_create_table = @“CREATE TABLE IF NOT EXISTS NONOTABLE 省略~~~~~~~~~~~~~”;
  13.  int result1 ;
  14.  //sqlite_exec用了针对sqlite3运行任何不要返回数据的命令,它用于执行更新,插入和删除。简单来说,这个方法执行的都是一些无需返回数据(虽然我们可能获取一个状态值。)。 
  15.  result1 = sqlite3_exec(database, [sql_create_table UTF8String], NULL, NULL, &errorMsg);
  16.  //检索查询操作 
  17.  int result2 ;
  18.  sqlite3_stmt *statment;
  19.  NSString *sql_selected = @“查询语句”;
  20. result2 = sqlite3_prepare_v2(database, [sql_selected UTF8String], -1, &statment, nil);
  21.  if(result2 == SQLITE_OK){
  22.      //单步操作 
  23.      while (sqlite3_step(statment) == SQLITE_ROW) {
  24.          int row = sqlite3_column_int(statment, 0);
  25.          char * rpwData = sqlite3_column_text(statment, 1);
  26.      }
  27.      sqlite3_finalize(statment);
  28.  }
  29.  //绑定变量,既就是插入操作的一种变种,比如我么那上面提到sqlite_exec可以执行插入操作,插入内容直接是写在sql字窜里,但是考虑到字窜涉及到无效的符号以及会一些严重的注入漏洞(比如以前听过的引号符号)。 
  30.  NSString *sql_bind = @“insert into foo value(?,?)”;
  31.  result2 = sqlite3_prepare_v2(database, [sql_selected UTF8String], -1, &statment, nil);
  32.  if(result2 == SQLITE_OK){
  33.      sqlite3_bind_int(statment, 1, 235);
  34.      sqlite3_bind_text(statment, 2, “test”, -1, nil);
  35.            sqlite3_finalize(statment);
  36.  }
  37.  if (sqlite3_step(statment) != SQLITE_DONE)
  38.      NSLog(@“error”);
  39.  sqlite3_finalize(statment);
  40.  sqlite3_close(database);
  1. //数据库操作
  2. sqlite3 *database;
  3. // const NSString * dbname = @”mydb”
  4. int result;
  5. //打开一个指定路径的现有的数据库,如果没有则会新建一个db库
  6. result = sqlite3_open([myFile UTF8String], &database);
  7. if (result != SQLITE_OK) {
  8. sqlite3_close(database);
  9. }
  10. //创建一个db表
  11. char *errorMsg;
  12. NSString *sql_create_table = @“CREATE TABLE IF NOT EXISTS NONOTABLE 省略~~~~~~~~~~~~~”;
  13. int result1 ;
  14. //sqlite_exec用了针对sqlite3运行任何不要返回数据的命令,它用于执行更新,插入和删除。简单来说,这个方法执行的都是一些无需返回数据(虽然我们可能获取一个状态值。)。
  15. result1 = sqlite3_exec(database, [sql_create_table UTF8String], NULL, NULL, &errorMsg);
  16. //检索查询操作
  17. int result2 ;
  18. sqlite3_stmt *statment;
  19. NSString *sql_selected = @“查询语句”;
  20. result2 = sqlite3_prepare_v2(database, [sql_selected UTF8String], -1, &statment, nil);
  21. if(result2 == SQLITE_OK){
  22. //单步操作
  23. while (sqlite3_step(statment) == SQLITE_ROW) {
  24. int row = sqlite3_column_int(statment, 0);
  25. char * rpwData = sqlite3_column_text(statment, 1);
  26. }
  27. sqlite3_finalize(statment);
  28. }
  29. //绑定变量,既就是插入操作的一种变种,比如我么那上面提到sqlite_exec可以执行插入操作,插入内容直接是写在sql字窜里,但是考虑到字窜涉及到无效的符号以及会一些严重的注入漏洞(比如以前听过的引号符号)。
  30. NSString *sql_bind = @“insert into foo value(?,?)”;
  31. result2 = sqlite3_prepare_v2(database, [sql_selected UTF8String], -1, &statment, nil);
  32. if(result2 == SQLITE_OK){
  33. sqlite3_bind_int(statment, 1, 235);
  34. sqlite3_bind_text(statment, 2, “test”, -1, nil);
  35. sqlite3_finalize(statment);
  36. }
  37. if (sqlite3_step(statment) != SQLITE_DONE)
  38. NSLog(@“error”);
  39. sqlite3_finalize(statment);
  40. sqlite3_close(database);

关于更多的,大伙可以自行百度,因为数据库的操作语法太怪异了,书上说是基本是基于c的,本人没学过c。看得有点心烦~

4。Core Data存储机制

大致浏览下基本感觉就是将对象归档搞成了可视化和简单化。

这块内容比较多。网上资料也挺丰富的。

暂时不做介绍了。

总结下:其实对于ios数据存储,*常用和主要要掌握的就是属性列表和数据库,因为两个是出镜率比较高的。

其他可能在数据存明显体现出储优势时,我们会去考虑用另外两种机制。

基础的来说,必须掌握属性列表和sqlite的操作存储。

2021年3月中国采购经理指数运行情况

国家统计局服务业调查中心中国物流与采购联合会   一、中国制造业采购经理指数运行情况   3月份,中国制造业采购经理指数(PMI)为51.9%,高于上月1.3个百分点,制造业景气回升。    从企业规模看,大、中、小型企业PMI分别为52.7%、51.6%和50.4%,较上月上升0.5、2.0和2.1个百分点,均高于临界点。   从分类指数看,在构成制造业PMI的5个分类指数中,生产指数、新订单指数、从业人员指数均高于临界点,供应商配送时间指数位于临界点,原材料库存指数低于临界点。   生产指数为53.9%,比上月上升2.0个百分点,高于临界点,表明制造业生产扩张步伐加快。   新订单指数为53.6%,比上月上升2.1个百分点,高于临界点,表明制造业市场需求增长较快。   原材料库存指数为48.4%,比上月回升0.7个百分点,低于临界点,表明制造业主要原材料库存量降幅收窄。   从业人员指数为50.1%,比上月上升2.0个百分点,高于临界点,表明制造业企业用工量较上月略有增加。   供应商配送时间指数为50.0%,比上月上升2.1个百分点,但位于临界点,表明制造业原材料供应商交货时间与上月相比变化不大。 表1  中国制造业PMI及构成指数(经季节调整)                                              单位:%   PMI   生产 新订单 原材料 库存 从业人员 供应商配送时间 2020年3月 52.0 54.1 52.0 49.0 50.9 48.2 2020年4月 50.8 53.7 50.2 48.2 50.2 50.1 2020年5月 50.6 53.2 50.9 47.3 49.4 50.5 2020年6月 50.9 53.9 51.4 47.6 49.1 50.5 2020年7月 51.1 54.0 51.7 47.9 49.3 50.4 2020年8月 51.0 53.5 52.0 47.3 49.4 50.4 2020年9月 51.5 54.0 52.8 48.5 49.6 50.7 2020年10月 51.4 53.9 52.8 48.0 49.3 50.6 2020年11月 52.1 54.7 53.9 48.6 49.5 50.1 2020年12月 51.9 54.2 53.6 48.6 49.6 49.9 2021年1月 51.3 53.5 52.3 49.0 48.4 48.8 2021年2月 50.6 51.9 51.5 47.7 48.1 47.9 2021年3月 51.9 53.9 53.6 48.4 50.1 50.0  表2  中国制造业PMI其他相关指标情况(经季节调整)                                              单位:%   新出口 订单 进口 采购量 主要原材料购进价格 出厂 价格 产成品 库存 在手 订单 生产经营活动预期 2020年3月 46.4 48.4 52.7 45.5 43.8 49.1 46.3 54.4 2020年4月 33.5 43.9 52.0 42.5 42.2 49.3 43.6 54.0 2020年5月 35.3 45.3 50.8 51.6 48.7 47.3 44.1 57.9 2020年6月 42.6 47.0 51.8 56.8 52.4 46.8 44.8 57.5 2020年7月 48.4 49.1 52.4 58.1 52.2 47.6 45.6 57.8 2020年8月 49.1 49.0 51.7 58.3 53.2 47.1 46.0 58.6 2020年9月 50.8 50.4 53.6 58.5 52.5 48.4 46.1 58.7 2020年10月 51.0 50.8 53.1 58.8 53.2 44.9 47.2 59.3 2020年11月 51.5 50.9 53.7 62.6 56.5 45.7 46.7 60.1 2020年12月 51.3 50.4 53.2 68.0 58.9 46.2 47.1 59.8 2021年1月 50.2 49.8 52.0 67.1 57.2 49.0 47.3 57.9 2021年2月 48.8 49.6 51.6 66.7 58.5 48.0 46.1 59.2 2021年3月 51.2 51.1 53.1 69.4 59.8 46.7 46.6 58.5    二、中国非制造业采购经理指数运行情况   3月份,非制造业商务活动指数为56.3%,较上月上升4.9个百分点,表明非制造业扩张有所加快。    分行业看,建筑业商务活动指数为62.3%,高于上月7.6个百分点。服务业商务活动指数为55.2%,高于上月4.4个百分点。从行业情况看,调查的21个服务业行业商务活动指数均高于临界点,其中铁路运输、航空运输、电信广播电视卫星传输服务、互联网软件及信息技术服务、货币金融服务、保险等行业商务活动指数位于60.0%以上高位景气区间。     新订单指数为55.9%,比上月上升7.0个百分点,表明非制造业市场需求较上月增加。分行业看,建筑业新订单指数为59.0%,比上月上升5.6个百分点;服务业新订单指数为55.4%,比上月上升7.3个百分点。   投入品价格指数为56.2%,比上月上升1.5个百分点,表明非制造业企业用于经营活动的投入品价格总体上涨。分行业看,建筑业投入品价格指数为63.6%,比上月上升3.4个百分点;服务业投入品价格指数为54.8%,比上月上升1.1个百分点。   销售价格指数为52.2%,比上月上升2.1个百分点,表明非制造业销售价格总体上涨幅度有所加大。分行业看,建筑业销售价格指数为55.0%,比上月上升2.5个百分点;服务业销售价格指数为51.7%,比上月上升2.0个百分点。   从业人员指数为49.7%,比上月回升1.3个百分点,表明非制造业用工景气度继续回升。分行业看,建筑业从业人员指数为55.0%,比上月上升0.8个百分点;服务业从业人员指数为48.8%,比上月回升1.4个百分点。   业务活动预期指数为63.7%,虽比上月回落0.3个百分点,但继续位于高位景气区间,表明非制造业企业对行业发展保持乐观。分行业看,建筑业业务活动预期指数为68.1%,比上月微落0.1个百分点;服务业业务活动预期指数为62.9%,比上月回落0.3个百分点。 表3  中国非制造业主要分类指数(经季节调整)                                             单位:%    商务活动 新订单 投入品 价格 销售价格 从业人员 业务活动 预期 2020年3月 52.3 49.2 49.4 46.1 47.7 57.3 2020年4月 53.2 52.1 49.0 45.4 48.6 60.1 2020年5月 53.6 52.6 52.0 48.6 48.5 63.9 2020年6月 54.4 52.7 52.9 49.5 48.7 60.3 2020年7月 54.2 51.5 53.0 50.1 48.1 62.2 2020年8月 55.2 52.3 51.9 50.1 48.3 62.1 2020年9月 55.9 54.0 50.6 48.9 49.1 63.0 2020年10月 56.2 53.0 50.9 49.4 49.4 62.9 2020年11月 56.4 52.8 52.7 51.0 48.9 61.2 2020年12月 55.7 51.9 54.3 52.3 48.7 60.6 2021年1月 52.4 48.7 54.5 51.4 47.8 55.1 2021年2月 51.4 48.9 54.7 50.1 48.4 64.0 2021年3月 56.3 55.9 56.2 52.2 49.7 63.7  表4  中国非制造业其他分类指数(经季节调整)                                              单位:%   新出口订单 在手订单 存货 供应商配送时间 2020年3月 38.6 43.0 46.1 46.4 2020年4月 35.5 43.4 47.0 51.0 2020年5月 41.3 44.3 47.8 52.9 2020年6月 43.3 44.8 48.0 52.1 2020年7月 44.5 44.9 48.1 51.9 2020年8月 45.1 44.6 48.5 52.4 2020年9月 49.1 46.3 48.5 52.2 2020年10月 47.0 44.9 48.7 52.3 2020年11月 49.0 45.2 48.8 51.8 2020年12月 47.5 44.7 47.0 51.2 2021年1月 48.0 44.0 47.4 49.8 2021年2月 45.7 44.0 45.9 49.8 2021年3月 50.3 45.9 48.2 51.8    三、中国综合PMI产出指数运行情况   3月份,综合PMI产出指数为55.3%,比上月上升3.7个百分点,表明我国企业生产经营活动总体加快。    附注   1.主要指标解释   采购经理指数(PMI),是通过对企业采购经理的月度调查结果统计汇总、编制而成的指数,它涵盖了企业采购、生产、流通等各个环节,包括制造业和非制造业领域,是国际上通用的监测宏观经济走势的先行性指数之一,具有较强的预测、预警作用。综合PMI产出指数是PMI指标体系中反映当期全行业(制造业和非制造业)产出变化情况的综合指数。PMI高于50%时,反映经济总体较上月扩张;低于50%,则反映经济总体较上月收缩。   2.调查范围   涉及《国民经济行业分类》(GB/T4754-2017)中制造业的31个行业大类,3000家调查样本;非制造业的43个行业大类,4200家调查样本。   3.调查方法   采购经理调查采用PPS(Probability Proportional to Size)抽样方法,以制造业或非制造业行业大类为层,行业样本量按其增加值占全部制造业或非制造业增加值的比重分配,层内样本使用与企业主营业务收入成比例的概率抽取。   本调查由国家统计局直属调查队具体组织实施,利用国家统计联网直报系统对企业采购经理进行月度问卷调查。   4.计算方法   (1)分类指数的计算方法。制造业采购经理调查指标体系包括生产、新订单、新出口订单、在手订单、产成品库存、采购量、进口、主要原材料购进价格、出厂价格、原材料库存、从业人员、供应商配送时间、生产经营活动预期等13个分类指数。非制造业采购经理调查指标体系包括商务活动、新订单、新出口订单、在手订单、存货、投入品价格、销售价格、从业人员、供应商配送时间、业务活动预期等10个分类指数。分类指数采用扩散指数计算方法,即正向回答的企业个数百分比加上回答不变的百分比的一半。由于非制造业没有合成指数,国际上通常用商务活动指数反映非制造业经济发展的总体变化情况。   (2)制造业PMI指数的计算方法。制造业PMI是由5个扩散指数(分类指数)加权计算而成。5个分类指数及其权数是依据其对经济的先行影响程度确定的。具体包括:新订单指数,权数为30%;生产指数,权数为25%;从业人员指数,权数为20%;供应商配送时间指数,权数为15%;原材料库存指数,权数为10%。其中,供应商配送时间指数为逆指数,在合成制造业PMI指数时进行反向运算。   (3)综合PMI产出指数的计算方法。综合PMI产出指数由制造业生产指数与非制造业商务活动指数加权求和而成,权数分别为制造业和非制造业占GDP的比重。   5.季节调整   采购经理调查是一项月度调查,受季节因素影响,数据波动较大。现发布的指数均为季节调整后的数据。 

Pandas常用功能笔记

Pandas常用功能笔记

pandas包导入
import pandas as pd
单个sheet处理
# 读取excel数据
file = ‘./aa.xlsx’
data_1 = pd.read_excel(file)
data_1.to_excel(file_save, index=False, header=False)

# 写入excel数据
file_save = ‘./bb.xlsx’
sample_1 = [(1, 2), (3, 4), (5, 6)]
data_2 = pd.Data_Frame(sample_1)
data_2.to_excel(file_save, index=False, header=[‘odd’, ‘even’])

sample_2 = {‘odd’: [1, 3, 5], ‘even’: [2, 4, 6]}
data_3 = pd.DataFrame(sample_2)
data_3.to_excel(file_save, index=False, header=True)
多个sheet处理
# 读取sheet数据
file = ‘./aa.xlsx’
data_1 = pd.read_excel(file, sheet=’data_1′)

# 写入sheet数据
f_save = pd.ExcelWriter(‘./bb.xlsx’)
sample_1 = [(1, 2), (3, 4), (5, 6)]
sheet_1 = pd.Data_Frame(sample_1)
sheet_1.to_excel(file_save, index=False, header=[‘odd’, ‘even’], sheet_name=’tuple_data’)

sample_2 = {‘odd’: [1, 3, 5], ‘even’: [2, 4, 6]}
sheet_2 = pd.DataFrame(sample_2)
sheet_2.to_excel(file_save, index=False, header=True, sheet_name=’dict_data’)
数据选择
file = r’./aa.xlsx’
df = pd.read_excel(file)
odd_data = df[‘odd’]
odd_list = df[‘odd’].values.to_list()

# 按名称选择
odd, even = df.loc([:, [‘odd’, ‘even’]])
odd_part, even_part = df.loc([0:2, [‘odd’, ‘even’]]) # 选取前两行

# 按列顺序选择
odd, even = df.iloc([:, [0, 1]])
odd_part, even_part = df.iloc([0:2, [0, 1]])
数据去重
file = r’./aa.xlsx’
df = pd.read_excel(file)
df.drop_duplicates(subset=[‘odd’,’even’],keep=’first’,inplace=True) # 在原数据上操作

# 不在原数据上操作,重新生成副本
new_data = drop_duplicates(subset=[‘odd’,’even’],keep=’first’,inplace=False)
遍历excel中的所有sheet
file = r’./aa.xlsx’
df = pd.ExcelFile(file)

for name in df.sheet_names:
sheet = pd.read_excel(file, sheet_name=name)
……

python实现固定尺寸图像拼接

python实现固定尺寸图像拼接

python实现固定尺寸图像拼接
讲解
1、代码效果:固定尺寸图像拼接
代码

import os
import cv2
import numpy as np

def joint(or_path, tar_path, size):
determination = tar_path
if not os.path.exists(determination):
os.makedirs(determination)

path = or_path
folders = os.listdir(path)
folders_name = []
folders.sort(key = lambda x: int(x.split(‘.’)[0]))
for folder in folders:
folders_name.append(path + “\\” + str(folder))
foldler_len = len(folders_name)
joint = []
for i in range(size):
joint.append(cv2.imread(folders_name[i]))
index = 0
for i in range(len(folders_name)):
if i < size:
continue
image = cv2.imread(folders_name[i])
joint[index] = np.hstack((joint[index], image))
index += 1
if index > size – 1:
index = 0
for i in range(1, len(joint)):
joint[0] = np.vstack((joint[0], joint[i]))
cv2.imwrite(tar_path + “/all.jpg”, joint[0])
print(joint[0].shape)

if __name__ == “__main__”:
joint(r””, r””, size)#原图像文件夹路径、新图像存放路径、单方向图像数

运行结果
运行前%title插图%num

运行后

%title插图%num

ArcPy基础之字典(二)

ArcPy基础之字典(二)

文章目录
一、编译环境和编译器
二、字典
1.定义字典,获取字典关键字、值、对象
2.字典的操作:增、删、改、查、复制和创建
一、编译环境和编译器
ArcGIS10.6:Python2.7.14 32-bit
编译器:Visual Studio Code

二、字典
1.定义字典,获取字典关键字、值、对象
代码如下(示例):

# encoding=utf-8

#定义字典{key:value}
dicA={“name”:”Xiao Ming”,”age”:14,”high”:167,”other”:””}
print(dicA)
print(len(dicA))#打印字典长度
#获取关键字、值、对象
print(dicA.keys())#打印字典关键字
print(type(dicA.keys()))#字典关键字类型,list

print(dicA.values())#打印值
print(type(dicA.values()))#字典值类型,list

print(dicA.items())#打印对象
print(type(dicA.items()))#字典对象类型,list
print(“———-0”)

#通过循环获取,以获取对象为例
for item in dicA.items():
print(item)

print(“———-1”)

#根据关键字获取值
print(dicA.get(“name”))#根据关键字”name”获取值Xiao Ming
dicA.get(“name”)#当获取的关键字在原字典中不存在时,get()会返回默认值,但不改变原字典。
print(dicA)

print(dicA.setdefault(‘name’))#根据关键字”name”获取名字Xiao Ming
dicA.setdefault(‘grades’)#当获取的关键字在原字典中不存在时,setdefaul()t会返回默认值None并更新字典
print(dicA)

打印结果:

{‘high’: 167, ‘age’: 14, ‘other’: ”, ‘name’: ‘Xiao Ming’}
4

[‘high’, ‘age’, ‘other’, ‘name’]
<type ‘list’>
[167, 14, ”, ‘Xiao Ming’]
<type ‘list’>
[(‘high’, 167), (‘age’, 14), (‘other’, ”), (‘name’, ‘Xiao Ming’)]
<type ‘list’>
———-0
(‘high’, 167)
(‘age’, 14)
(‘other’, ”)
(‘name’, ‘Xiao Ming’)
———-1
Xiao Ming
{‘high’: 167, ‘age’: 14, ‘other’: ”, ‘name’: ‘Xiao Ming’}
Xiao Ming
{‘high’: 167, ‘age’: 14, ‘other’: ”, ‘grades’: None, ‘name’: ‘Xiao Ming’}

2.字典的操作:增、删、改、查、复制和创建
dicA={‘high’: 167, ‘age’: 14, ‘other’: ”, ‘grades’: None, ‘name’: ‘Xiao Ming’}
#字典的操作
#增
dicA[“sex”]=”boy”#用[]直接添加字典关键字和值,且增加在字典尾部
print(dicA)#打印增加后字典

dicB={“class”:1,34:”ge”}
dicA.update(dicB)#将dicB更新到dicA中
print(dicA)#打印更新后结果
print(“———-2”)

#删
del dicA[“other”]#通过del删除关键词和值
print(dicA)#打印删除后字典

dicA.pop(“age”)#索引并删除关键字为“age”的键值对
print(dicA)#打印pop后字典

dicA.popitem()#索引并删除字典*新加入的键值对
print(dicA)#打印popitem后结果
print(“———-3”)

#改
dicA[“sex”]=”girl”#通过关键字将性别改为girl,它会覆盖原来值
print(dicA)#打印改后字典
print(“———-4”)

#查
print dicA.has_key(“sex”)#查询字典中是否存在关键字sex
print(“———-5”)

#字典的复制
print(dicA.copy())#打印复制后字典
print(“———-6”)

#创建新字典
dic1={}#新建一个空的字典
dic2=dic1.fromkeys((“hair”,”leg”),”long”)#对空的字典添加”hair”和“leg”两个关键值,并将值都赋为long
print(dic2)#打印新建字典

#清除
dic2.clear()#清除dic2
print(dic2)

打印结果:

{‘name’: ‘Xiao Ming’, ‘age’: 14, ‘sex’: ‘boy’, ‘high’: 167, ‘grades’: None, ‘other’: ”}
{34: ‘ge’, ‘name’: ‘Xiao Ming’, ‘age’: 14, ‘sex’: ‘boy’, ‘high’: 167, ‘grades’: None, ‘other’: ”, ‘class’: 1}
———-2
{34: ‘ge’, ‘name’: ‘Xiao Ming’, ‘age’: 14, ‘sex’: ‘boy’, ‘high’: 167, ‘grades’: None, ‘class’: 1}
{34: ‘ge’, ‘name’: ‘Xiao Ming’, ‘sex’: ‘boy’, ‘high’: 167, ‘grades’: None, ‘class’: 1}
{‘name’: ‘Xiao Ming’, ‘sex’: ‘boy’, ‘high’: 167, ‘grades’: None, ‘class’: 1}
———-3
{‘name’: ‘Xiao Ming’, ‘sex’: ‘girl’, ‘high’: 167, ‘grades’: None, ‘class’: 1}
———-4
True
———-5
{‘high’: 167, ‘class’: 1, ‘grades’: None, ‘name’: ‘Xiao Ming’, ‘sex’: ‘girl’}
———-6
{‘hair’: ‘long’, ‘leg’: ‘long’}
{}

ArcGIS开发arcpy教程
07-25
<p> arcgis数据处理过程中使用到的arcpy脚本,基础教程。有助于gis从业人员开发arcpy脚本便捷处理数据。提供基础与案例应用结合方式,讲解arcpy知识。 </p> <p> 1.python基础 </p> <p> 2.基本图形创建 </p> <p> 3.shapefile数据操作 </p> <p> 4.常见数据txt.csv,json,excel数据操作 </p> <p> 5.工具打包 </p> <p> (1)添加脚本方式 </p> <p> (2)ArcCatalog添加代码方式 </p> <p> 6.arcpy案例应用 </p> <p> (1)几何图形面polygon,点point之间关系应用 </p> <p> (2)面四至坐标提取应用 </p> <p> (3)面求交,找出*大相交面应用 </p> <p> (4)逐条记录导出应用 </p> <p> (5)批量裁剪影像应用 </p> <p> (6)栅格数据批量定义投影以及工具打包应用 </p> <p> <br /></p>
arcgis二次开发arcpy视频教程(持续更新中……)
yGIS
3253
https://edu.csdn.net/course/detail/25535 01. 课程概况 02. ArcPy开发-1-python基础–环境搭建、*个程序、练习 03. ArcPy开发-2-python基础–表达式,数据类型、语句、变量 04. ArcPy开发-3-python基础–字符串 05. ArcPy开发-4-python基础–列表 06. ArcPy…

爱码士看,月亮在跳舞:好文章2 小时前回复

爱码士LaoYuanPython:欢迎博主加入CSDN!欢迎博主到本人的Python专栏来交流!2 小时前回复

相关推荐
python笔记之ArcPy简介
baoqian1993的博客
2万+
第1章 ArcPy简介 1.1什么是 ArcPy? ArcPy 是一个以成功的 arcgisscripting 模块为基础并继承了 arcgisscripting 功能进而构建而成的站点包。目的是为以实用高效的方式通过 Python 执行地理数据分析、数据转换、数据管理和地图自动化创建基础。 该包提供了丰富纯正的 Python 体验,具有代码自动完成功能(输入关键字和点即可获得
ArcPy*章-Python基础
28
学习Arcpy,从零开始积累。1.代码注释: python中,说明部分通常使用注释来实现: 方式: # 或者 ## + 注释部分内容2. 模块导入: 方式: import Eg: import arcpy import os3.变量:(python中定义变量,不需要先声明变量类型,只需要直接命名和赋值即可) Eg: mapsize = “22 x 34″4.内置数据类型:…
python3使用arcpy_Python & ArcPy – 随笔分类 – McDelfino – 博客园
weixin_39622138的博客
109
随笔分类 – Python & ArcPyPython 学习,主要是 ArcGIS 里面有用到!摘要:利用 sklearn.feature_extraction.text 中的 CountVectorizer 来实现 首先获取所有的文本信息 然后将文本信息转化为从 0 开始的数字 获取转换后的字符向量 参见如下代码: >>> text_01 = “My name is A…
视频教程-ArcGIS开发arcpy教程-其他
weixin_33329105的博客
229
ArcGIS开发arcpy教程 从事数字城市、智慧城市、地质灾害、警务等国土…
从Python到空间分析Arcpy || 1.2.1 数据类型是什么,有哪些
jieyoudata的博客
66
今天开始介绍python中的数据类型,这是python中*基础也是*核心的内容,计划分为4-5个小节来介绍。 总的来说python中的数据类型可以分为 9大类(很容易记的数字)。 数值类型(Number) 数值类型又包括:int 整型 float 浮点型 complex复数类型,常用前2个 布尔型(Bool ) 布尔类型在python中是用关键字来定义的,False 和True ,请注意两个单词的*个字母都要大写,python 语言本身是对大小写敏感的语言,A和a在python中是两个不同的对象,bool
access数据放到list中_从Python到空间分析Arcpy || 1.2.1 数据类型

【总结】2020年*新算法工程师技术路线图

这是一份写给公司算法组同事们的技术路线图,其目的主要是为大家在技术路线的成长方面提供一些方向指引,配套一些自我考核项,可以带着实践进行学习,加深理解和掌握。

%title插图%num

工程师能力层级概览

对于不同级别的算法工程师技能要求,我们大致可以分成以下几个层级:

  • 初级:可以在一些指导和协助下独立完成开发任务。具体到算法方面,需要你对于工具框架,建模技术,业务特性等方面有一定的了解,可以独立实现一些算法项目上的需求。
  • 中级:可以基本独立完成一个项目的开发与交付。在初级工程师的基础上,对于深入了解技术原理的要求会更高,并且能够应对项目中各种复杂多变的挑战,对于已有技术和工具进行改造适配。在整体工程化交付方面,对于代码质量,架构设计,甚至项目管理方面的要求会开始显现。另外从业务出发来评估技术选型和方案也变得尤为重要。
  • 高级:可以独立负责一条产品线的运作。在中级工程师的基础上,需要更广阔的技术视野与开拓创新能力,定义整个产品线的前进方向。解决问题已经不是关键,更重要的是提出和定义问题,能够打造出在业界具有*性和差异性的产品,为公司创造更大的价值。

事实上对于不同层级的工程师,非技术部分的要求都有一定占比。本文主要聚焦在技术路线图上,对于其他方面的学习进阶路线不会做覆盖。

阅读建议

以下内容分工程基础,算法基础,算法工程交叉,工程深入方向,算法深入方向几个部分,在各个部分内部会进一步区分一些主题。在各个主题内部,也是有深入程度的区别的,不过限于篇幅没有进行详细的说明。建议学习路线可以先把两个基础部分与工作中较为相关的内容做一个整体基础的夯实,然后可以在后续交叉和深入方向的主题中选择感兴趣的进行深入了解和学习,过程中发现基础部分欠缺的,可以再回到基础部分查漏补缺,迭代前行。

工程基础

编程语言

Python

Python 是算法工程师日常工作中*常用的语言,应该作为必须掌握的一门技术。大致的学习路线如下:

  • 学习掌握 Python 的基本语法,可以通过各类入门教程来看,个人推荐《Learn Python the Hard Way》。
  • 自我考核:能够读懂大多数的内部项目及一些开源项目代码的基本模块,例如 pandas, sklearn 等。
  • 学习 Python 的编程风格,建议学习观远内部的 Python 代码规范。
  • 自我考核:编写的代码符合编码规范,能够通过各类 lint 检查。
  • Python 进阶,这方面有一本非常著名的书《Fluent Python》,深入介绍了 Python 内部的很多工作原理,读完之后对于各类疑难问题的理解排查,以及语言高级特性的应用方面会很有帮助。另外动态语言元编程这块,《Ruby 元编程》也是一本非常值得推荐的书。
  • 自我考核:能够读懂一些复杂的 Python 项目,例如 sqlalchemy 中就大量使用了元编程技巧。在实际工程项目中,能够找到一些应用高级技巧的点进行实践,例如基于 Cython 的性能优化等。
  • 领域应用,Python 的应用相当广泛,在各个领域深入下去都有很多可以学习的内容,比如 Web 开发,爬虫,运维工具,数据处理,机器学习等。这块主要就看大家各自的兴趣来做自由选择了,个人推荐熟悉了解一下 Python web 开发,测试开发相关的内容,开拓视野。
  • 自我考核:以 Web 开发和测试开发为例,尝试写一个简单的 model serving http 服务,并编写相应的自动化测试。

Scala/Java

Java 目前是企业级开发中*常用的软件,包括在大数据领域,也是应用*广泛的语言,例如当年的 Hadoop 生态基本都是基于 Java 开发的。Scala 由于其函数式编程的特性,在做数据处理方面提供了非常方便的 API,也因为 Spark 等项目的火热,形成了一定的流行度。在进行企业级的软件开发,高性能,大规模数据处理等方面,JVM 上的这两门语言有很大的实用价值,值得学习。

顺带一提,Scala 本身是一门非常有意思的语言,其中函数式编程的思想与设计模式又是非常大的一块内容,对于拓宽视野,陶冶情操都是挺不错的选择。

考虑到算法工程师的工作内容属性,这边给出一个 Scala 的学习路线:

  • 学习掌握 Scala 的基本语法,开发环境配置,项目编译运行等基础知识。这里推荐 Coursera 上 Martin Odersky 的课程,《快学 Scala》或《Programming in Scala》两本书也可以搭配着浏览参考。
  • 自我考核:能使用 Scala 来实现一些简单算法问题,例如 DFS/BFS。或者使用 Scala 来处理一些日常数据工作,例如读取日志文件,提取一些关键信息等。
  • 学习使用 Scala 来开发 Spark 应用,推荐 edX 上的《Big Data Analytics Using Spark》或者 Coursera 上的《Big Data Analytics with Scala and Spark》,另外有些相关书籍也可以参考,比如《Spark 快速大数据分析》等。
  • 自我考核:能够使用 Spark 的 Scala API 来进行大规模的数据分析及处理,完成 lag feature 之类的特征工程处理。
  • JVM 的原理学习,Scala/Java 都是 JVM 上运行的优秀语言,其背后是一个非常大的生态,包括在 Web,Android,数据基础架构等方面有广泛的应用。JVM 相比 Python 虚拟机,发展更加成熟,有一套非常完善的 JDK 工具链及衍生的各类项目,便于开发者 debug,调优应用。这方面推荐学习周志明的《深入理解 Java 虚拟机》。
  • 自我考核:理解 JVM GC 原理,能通过 JDK 中相关工具或者优秀的第三方工具如 arthas 等,排查分析 Spark 数据应用的资源使用情况,GC profiling,hot method profiling 等,进而进行参数优化。
  • 计算机语言理论。Programming Language 作为计算机科学的一个重要分支,包含了很多值得深入研究的主题,例如类型论,程序分析,泛型,元编程,DSL,编译原理等。这方面的很多话题,在机器学习方面也有很多实际应用,比如 TVM 这类工作,涉及到大量编译原理的应用,知乎大佬 “蓝色” 也作为这个领域的专家在从事深度学习框架相关的工作。llvm, clang 作者 Chris Lattner 也加入 Google 主导了 Swift for Tensorflow 等工作。Scala 作为一门学术范非常强的语言,拥有*佳的 FP,元编程等能力支持,强大的类型系统包括自动推理,泛型等等高级语言特性,相对来说是一门非常 “值得” 学习的新语言,也是一个进入 PL 领域深入学习的 “gateway drug” 🙂 对这个方面有兴趣的同学,可以考虑阅读《Scala 函数式编程》,《冒号课堂》,以及 Coursera 上《Programming Languages》也是一门非常好的课程。另外只想做科普级了解的同学,也可以读一读著名的《黑客与画家》感受一下。

C/C++/Rust

当前流行的算法框架,例如 TensorFlow, PyTorch, LightGBM 等,底层都是基于 C++ 为主要语言进行实现的。但是 C++ 本身过于复杂,使用场景也比较有限制,建议只需要达到能够读懂一些基础的 C++ 代码逻辑即可。在系统级开发领域,目前有一门新语言逐渐崛起,连续几年被 StackOverflow 投票评选为程序员*喜爱的语言:Rust。从设计理念和一些业界应用(例如 TiKV)来看还是非常不错的,但是我也没有深入学习了解过,就不做具体推荐了。这方面建议的学习内容包括经典的《The C Programming Language》以及 Rust 官方的:https://github.com/rust-lang/rustlings

  • 自我考核:能够读懂 LightGBM 里对于 tweedie loss 的相关定义代码。

操作系统

基本概念

我们所编写的算法应用,都是通过操作系统的环境运行在物理硬件之上的。在实际运作过程中,会碰到不少相关的问题,例如为什么程序报了资源不足的错误,为什么 notebook 在浏览器里打不开,为什么进程 hang 住了没有响应等等,都需要一些操作系统的知识来帮助理解和分析问题,*终排查解决。操作系统涵盖的内容比较多,建议一开始只需要了解一些主要概念(例如硬件结构,CPU 调度,进程,线程,内存管理,文件系统,IO,网络等),对于整体图景有一些感觉即可。后续碰到了实际问题,可以再在各个部分深入学习展开。优秀的学习资料也有很多,基本都是大部头,重点推荐《深入理解计算机系统》,《Operating Systems: Three Easy Pieces》,以及《现代操作系统》。

  • 自我考核:能够基本明确运行一个模型训练任务过程中,底层使用到的硬件,操作系统组件,及其交互运作的方式是如何的。

Linux 基础

平时工作中*常用的两个操作系统 CentOS 和 macOS,都是 Unix/Linux 系的,因此学习掌握相关的基础知识非常重要。一些必须掌握的知识点包括:Shell 与命令行工具,软件包管理,用户及权限,系统进程管理,文件系统基础等。这方面的入门学习资料推荐《鸟哥的 Linux 私房菜》,基本涵盖了 Linux 系统管理员需要掌握知识的方方面面。进阶可以阅读《Unix 环境高级编程》,对于各种系统调用的讲解非常深入,可以为后续性能调优等高级应用打下基础。

  • 自我考核:开发一个 shell 小工具,实现一些日常工作需求,例如定时自动清理数据文件夹中超过一定年龄的数据文件,自动清理内存占用较大且运行时间较久的 jupyter notebook 进程等。

深入应用

工作中碰到的疑难问题排查,性能分析与优化,系统运维及稳定性工程等方面,都需要较为深入的计算机体系和操作系统知识,感兴趣的同学可以针对性的进行深入学习。以性能优化为例,可以学习经典的《性能之巅》,了解其中的原理及高级工具链。像其中的系统调用追踪 (strace),动态追踪(systemtap, DTrace, perf, eBPF) 等技术,对于操作系统相关的问题排查都会很有帮助。

  • 自我考核:能够分析定位出 LightGBM 训练过程中的性能瓶颈,精确到函数调用甚至代码行号的级别。

软件工程

算法与数据结构

暂时先把这块放到软件工程模块下。这里指的算法是计算机科学中的经典算法,例如递归,排序,搜索,动态规划等,有别于我们常说的机器学习算法。这块的学习资料网上有非常多,个人当年是通过普林斯顿的算法课 (需要有 Java 基础) 入门,后来又上了斯坦福的算法分析与设计,开拓了一些视野。书籍方面推荐新手从《算法图解》入门,然后可以考虑阅读 Jeff Erickson 的《Algorithms》,或者选择上面提到的网课。另外像《编程珠玑》,《编程之美》等也可以参阅,里面有不少问题的巧妙解法。除了从书本中学习,还可以直接去 LeetCode 等网站进行实战操作进行练习提高。

  • 自我考核:能够设计相关的数据结构,实现一个类似 airflow 中点击任意节点向后运行的功能。

代码规范

从初级程序员到中高级程序员,其中比较大的一个差异就是代码编写习惯上,从一开始写计算机能理解,能够运行成功的代码,逐渐演化到写人能够理解,易于修改与维护的代码。在这条学习路径上,首先需要建立起这方面的意识,然后需要在实战中反复思考和打磨自己的代码,评判和学习其它优秀的项目代码,才能逐渐精进。推荐的学习书籍有《编写可读代码的艺术》,一本非常短小精悍的入门书籍,后续可以再慢慢阅读那些经典大部头,例如《Clean Code》,《Code Complete》,《The Pragmatic Programmer》等。这方面 Python 也有一本比较针对性的书籍《Effective Python》,值得一读。

  • 自我考核:审视自己写的项目代码,能发现并修正至少三处不符合*佳编码实践的问题。

设计模式

在代码架构方面,设计模式是一个重要的话题,对于日常工作中出现的许多典型场景,给出了一些解决方案的“套路”。这方面*著名的书当属 GoF 的《设计模式》,不过个人并不十分推荐,尤其是以 Python 作为主要工作语言的话,其中很大部分的设计模式可能并不需要。入门可以浏览一下这个网站掌握一些基本概念:https://refactoringguru.cn/design-patterns/python ,后续可以考虑阅读《Clean Architecture》,《重构》等相关数据,理解掌握在优化代码架构过程中思考的核心点,并加以运用。Python 相关的设计模式应用,还可以参考《Python in Practice》。

  • 自我考核:在项目中,找到一处可以应用设计模式的地方,进行重构改进。

质量保障

对于需要实际上线运行的软件工程,质量保障是非常重要的一个环节,能够确保整个产品按照期望的方式进行运作。在机器学习项目中,由于引入了数据这个因素,相比传统的软件测试会有更高的难度,也是业界还在摸索前进的方向。建议可以先阅读《单元测试的艺术》或《Google 软件测试之道》,大致理解软件测试的一些基本概念和运作方式,在此基础上可以进一步阅读 Martin Fowler 对于机器学习领域提出的 CD4ML 中相关的测试环节,学习 sklearn,LightGBM 等开源库的测试开发方式,掌握机器学习相关的质量保障技术能力。

  • 自我考核:在项目中,实现基础的数据输入测试,预测输出测试。

项目管理

软件工程推进过程中,项目管理相关的技能方法与工具运用也非常的关键。其中各种研发流程与规范,例如敏捷开发,设计评审,代码评审,版本管控,任务看板管理等,都是实际项目推进中非常重要的知识技能点。这方面推荐学习一本经典的软件工程教材《构建之法》,了解软件项目管理的方方面面。进一步来说广义的项目管理上的很多知识点也是后续深入学习的方向,可以参考*客时间上的课程《项目管理实战 20 讲》。

  • 自我考核:在某个负责项目中运用项目管理方法,完成一个实际的需求评估,项目规划,设计与评审,开发执行,项目上线,监控维护流程,并对整个过程做复盘总结。

高级话题

软件工程师在技能方向成长的一条路线就是成为软件架构师,在这个方向上对于技能点会有非常高的综合性要求,其中也有不少高级话题需要深入学习和了解,例如技术选型与系统架构设计,架构设计原则与模式,宽广的研发知识视野,高性能,高可用,可扩展性,安全性等等。有兴趣的同学可以了解一下*客时间的《从 0 开始学架构》这门课,逐渐培养这方面的视野与能力。另外如《微服务架构设计模式》还有领域驱动设计方面的一系列书籍也值得参考学习。

  • 自我考核:设计一个算法项目 Docker 镜像自动打包系统。

算法基础

数据分析

数学基础

在进行算法建模时,深入了解数据情况,做各类探索性分析,统计建模等工作非常重要,这方面对一些数学基础知识有一定的要求,例如概率论,统计学等。这方面除了经典的数学教材,也可以参考更程序员向的《统计思维》,《贝叶斯方法》,《程序员的数学 2》等书籍。

  • 自我考核:理解实际项目中的数据分布情况,并使用统计建模手段,推断预测值的置信区间。

可视化

在进行数据分析时,可视化是一个非常重要的手段,有助于我们快速理解数据情况,发掘数据规律,并排查异常点。对于各种不同类型的数据,会对应不同的可视化*佳实践,如选择不同的图表类型,板式设计,分析思路编排,人机交互方式等等。另一方面,可视化与数据报告也是我们与不同角色人群沟通数据 insights 的一个重要途径,需要从业务角度出发去思考可视化与沟通方式。这方面可以参考《Storytelling with Data》,《The Visual Display of Quantitative Information》等经典数据,同时也需要培养自己的商业背景 sense,提升沟通能力。

  • 自我考核:对内沟通方面,能使用可视化技术,分析模型的 bad case 情况,并确定优化改进方向。对外沟通方面,能独立完成项目的数据分析沟通报告。

误差分析与调优

在做算法模型调优改进中,需要从数据分析的基础上出发来决定实验方向,这么做有几个好处:

  • 从分析出发指导调优更有方向性,而不是凭经验加个特征,改个参数碰运气。哪怕是业务方提供的信息,也*好是有数据分析为前提再做尝试,而不是当成一个既定事实。
  • 由分析发现的根源问题,对于结果验证也更有帮助。尤其在预测的数据量*大情况下,加一个单一特征很可能总体只有千分位准确率的提升,无法确定是天然波动还是真实的提升。但如果有分析的前提,那么我们可以有针对性的看对于这个已知问题,我们的调优策略是否生效,而不是只看一个总体准确率。
  • 对于问题的彻底排查解决也更有帮助,有时候结果没有提升,不一定是特征没用,也可能是特征代码有 bug 之类的问题。带着数据分析的目标去看为什么这个特征没有效果,是模型没学到还是特征没有区分度等,有没有改进方案,对于我们评判调优尝试是否成功的原因也更能彻查到底。
  • 数据分析会帮助我们发现一些额外的问题点,比如销量数据清洗处理是不是有问题,是不是业务本身有异常,需要剔除数据等。

这方面在业界有一些关于误差分析的探索研究,不过大多数都是基于分类问题的,例如《Identifying Unknown Unknowns in the Open World》,《A Characterization of Prediction Errors》等。可以在了解这些研究的基础上,结合具体的业务情况,深入思考总结误差分析的思路与方法论。

  • 自我考核:在项目中形成一套可以重复使用的误差分析方案,能够快速从预测输出中定位到目前模型*重要的误差类别,并一定程度上寻找到根本原因。

机器学习基础

传统机器学习

这块大家应该都非常熟悉了,初阶的学习路线可以参考周志华老师的《机器学习》,涵盖了机器学习基础,常用机器学习方法,和一些进阶话题如学习理论,强化学习等。如果希望深化理论基础,可以参考经典的《PRML》,《ESL》和《统计学习方法》。在实战中,需要综合业务知识,算法原理,及数据分析等手段,逐渐积累形成建模调优的方法论,提高整体实验迭代的效率和成功率。

  • 自我考核:结合实际业务和机器学习理论知识,挖掘项目中算法表现不够好的问题,并通过算法改造进行提升或解决。

深度学习

近些年兴起的深度学习,已经成为机器学习领域一个非常重要的分支,在各个应用方向发挥了很大的作用。相对于传统机器学习,对于特征工程要求的降低成了其核心优势。另一方面,深度学习对于大数据量,大规模算力的应用能力很强,也一定程度上提升了整体的产出效果。由于理论方面的研究稍显落后,深度学习在实际应用中对于使用者的经验技能要求相对比较高,需要有大量的实战经验才能达到比较理想的效果。这方面的学习资料推荐 Keras 作者的《Deep Learning with Python》,以及《Hands-on Machine Learning with Scikit-Learn and TensorFlow》,而在理论方面推荐著名的“花书”《Deep Learning》。在学习理论原理的基础上,尤其要注意在实际算法应用中,能够通过观察各种指标与数据分析,找到提升模型的操作改进方向。

  • 自我考核:能够在实际项目中,使用深度学习模型,达到接近甚至超过传统 GBDT 模型的精确度效果,或者通过 ensemble,embedding 特征方式,提升已有模型的精度。

领域建模

目前我们的业务领域在时间序列预测,自然语言处理,推荐等方面,其它类似图像,搜索,广告等领域也都有各自的一些领域建模方法。在时间序列领域,包括了传统时序模型,如 ARIMA, Prophet,机器学习模型,如划动窗口特征构建方法结合 LightGBM,及深度学习模型,例如 LSTM,seq2seq,transformer 等。这方面可以参考 Kaggle 上相关比赛的方案分享,以及 Amazon,Uber,天猫等有类似业务场景公司的分享资料。其它领域也是类似,通过了解历史技术演进,相关比赛,业界的方案分享与开源项目,会议论文来逐渐掌握学习建模方法,结合实际业务进行实践尝试,积累起更加体系性的个人知识技能。

  • 自我考核:在项目中复现一个 Kaggle 获胜方案,检验其效果,分析模型表现背后的原因,并尝试进行改进。

算法框架

数据处理框架

在项目实施过程中,会需要各类复杂的数据处理操作,因此熟练掌握此类框架就显得尤为重要。目前行业的标准基本上会参照 Pandas DataFrame 的定义,在数据量较大的情况下,也有许多类似的框架,如 Spark,Dask,Modin,Mars 等支持分布式运行的 DataFrame,以及 cuDF,Vaex 等提升单机性能的改进实现。这方面经典的书籍可以参考 Wes McKinney 的《Python for Data Analysis》,在掌握基础数据操作的基础上,可以进而了解窗口函数,向量化性能优化等高级话题。另外 SQL 也可以做非常复杂的数据处理工作,有不少公司例如阿里会以 SQL 为主来构建数据处理流程,感兴趣的同学也可以学习一下 SQL 中各种高级计算的使用及优化方法。

  • 自我考核:在已有项目中,能把至少三个使用 apply 方法的 pandas 处理修改成向量化运行,并测试性能提升。使用 window function 或其它方案来实现 lag 特征,减少 join 次数。

机器学习框架

机器学习方面的新框架层出不穷,一方面我们需要掌握经典框架的使用方式,理解其模块构成,接口规范的设计,一定程度上来说其它新框架也都需要遵循这些业界标准框架的模块与接口定义。另一方面对于新框架或特定领域框架,我们需要掌握快速评估,上手使用,并且做一定改造适配的能力。一些比较经典的框架有:

  • 通用机器学习:scikit-learn,Spark ML,LightGBM
  • 通用深度学习:Keras/TensorFlow,PyTorch
  • 特征工程:tsfresh, Featuretools,Feast
  • AutoML:hyperopt,SMAC3,nni,autogluon
  • 可解释机器学习:shap,aix360,eli5,interpret
  • 异常检测:pyod,egads
  • 可视化:pyecharts,seaborn
  • 数据质量:cerberus,pandas_profiling,Deequ
  • 时间序列:fbprophet,sktime,pyts
  • 大规模机器学习:Horovod,BigDL,mmlspark
  • Pipeline:MLflow, metaflow,KubeFlow,Hopsworks

一般的学习路径主要是阅读这些框架的官方文档和 tutorial,在自己的项目中进行尝试使用。对于一些核心接口,也可以阅读一下相关的源代码,深入理解其背后的原理。

  • 自我考核:在 LightGBM 框架下,实现一个自定义的损失函数,并跑通训练与预测流程。

其它框架

其它比较常见且与算法工程师日常工作会有一些联系的有 Web 框架,爬虫框架等,*具有代表性的当属 Flask 和 scrapy。这两者背后各自又是很大一块领域,尤其 web 开发更是保罗万象。感兴趣的同学还可以了解一下一些新兴的基于 Python3 的框架,例如 FastAPI,其背后借鉴的许多现代框架的思想设计,包括数据验证,序列化,自动文档,异步高性能等,开拓一下知识面。

  • 自我考核:实现一个简单的 model serving http 服务。

算法工程交叉

大规模算法运行

分布式训练

在很多项目中,数据量达到十亿级以上的情况下,单机训练会难以支撑。因此分布式训练也是实际工程落地中非常重要的一个主题。分布式训练涉及到多机的通讯协同方式,优化算法的改造,数据及模型的并行与聚合,以及框架的选择和运维等话题,具体可以参考《分布式机器学习》。另外对于分布式系统,也可以参阅《数据密集型应用系统设计》这本神作,了解其背后原理。

  • 自我考核:能够在多机上进行亿级数据的 GBDT 模型训练与预测。

高性能计算

在做大规模的数据训练与推理时,近些年涌现出许多高性能计算优化的方法,例如从硬件方面,有各种超线程技术,向量化指令集,GPGPU,TPU 的应用等,从软件方面,有针对数值计算场景的 OpenBLAS,有自动并行化的 OpenMP,有各种 codegen,JIT 技术下的运行时优化等。这方面可以学习的方向也很多,从基础的并行编程,编译原理及优化的知识开始,到 CUDA,OpenMP 的应用(例如 Nvidia 的 cuDNN,还有 LightGBM 中也用到了 OpenMP),Codegen,JIT 等技术在 Spark,TVM 等项目中的使用等,建议有深度性能优化需求时可以往这些方向做调研和学习。

  • 自我考核:能够通过 LLVM JIT 来优化实现 Spark window function 的执行性能。

模型加速领域

这个方向分两个部分,一块是模型训练方面,能够做到加速,例如使用大 batch size,迁移学习,持续的在线 / 增量学习等手段,另一块在模型预测方面,也有很多加速需求,比如模型参数量优化,模型压缩,混合精度,知识蒸馏等技术手段,都是为了做到更高性能,更低资源消耗的模型预测推理。这方面业界有各个方向的文章和技术实现可以参考,比如经典的《Training ImageNet in 1 Hour》,MobileNet,TensorRT,二值网络等。

  • 自我考核:在典型的销量预测场景中实现增量训练与预测。

MLOps

编排调度

包含各类 pipeline 的编排与调度能力的支持,包括数据 pipeline,训练 pipeline 和 serving pipeline 等。这方面比较常用的框架工具有 Airflow,DolphinScheduler,Cadence 等,需要掌握其基本的工作原理和使用方式,并能够应用于离线实验与线上运行。

  • 自我考核:使用 Airflow 完成一个标准的项目 pipeline 搭建与运行。

数据集成

相对于传统的 DevOps,机器学习项目*大的区别在于数据方面的依赖会更加显著与重要。这方面的话题包括数据血缘,数据质量保障,数据版本控制等,有各类工具可以借鉴使用,例如数据版本管理方面的 DVC,数据质量方面的 TFX Data Validation,Cerberus,Deequ 等。在方法论层面,《The ML Test Score》中给出了不少数据相关的具体测试方法,值得参考学习。

  • 自我考核:在项目中实现输入数据的分布测试,特征工程测试及特征重要性准入测试。

实验管理

这部分也是 ML 项目的独特之处,在开发过程中有大量的实验及相应的结果输出需要记录,以指导后续调整优化的方向,并选择*优结果来进行上线部署。这方面可以参考的项目有 MLflow,fitlog,wandb 等。当然对于单独的项目来说,可能 online Excel 就能满足需求了 🙂

  • 自我考核:在实际项目中实行一套标准的实验记录手段,并能从中找出各类实验尝试带来的精度提升的 top 5 分别是哪些操作。

Serving

目前我们的 serving 大多数是离线 batch 预计算的形式,所以主要依赖的技术手段是各类离线 inference 的方法,例如直接使用 model predict 接口,使用 mmlspark 等做大规模并行 inference 等。如果涉及到在线 serving,情况会更加复杂,例如在线 pipeline 的运行,实时特征获取,low latency/high throughput 的 serving 服务等,可以参考 TF Serving,MLeap,H2O,PredictionIO,PMML/PFA/ONNX 等开发标准模型格式等。

  • 自我考核:部署一个实时预测服务,能够根据用户输入产生相应的预测结果。

CI/CD

软件工程中的持续集成,持续部署已经成为一种标准实践,在算法项目中,额外引入了数据这个维度的复杂性,带来了一些新的挑战。在这个方向上,几个主要话题包括自动化测试,pipeline 打包部署,持续监控运维等,可以参考 Martin Fowler 关于 CD4ML 的文章。工具系统层面,可以学习传统的 Jenkins,也有一些新选择例如 CircleCI,GoCD,VerCD(Uber)等。

  • 自我考核:通过 Jenkins 实现 pipeline 自动测试,打包,上线流程。

系统监控

在整个项目上线后,需要对系统的各个环节进行监控,并对各种异常情况作出响应。例如输入数据的监控,判别测试数据与训练数据的分布是否有偏移,整个运行 pipeline 的监控,判别是否有运行失败抛出异常的情况,对于预测输出的监控,确保没有异常的预测输出值,也包括对于系统计算资源等方面的监控,确保不会因为资源不足导致业务受到影响等。在监控信息收集,基础上,还需要配套一系列的自动告警通知,日志追踪排查等。这方面的工具框架包括 TF data validation 这类专门针对算法项目的新产品,也有 elasicsearch + kibana 这类传统产品。

  • 自我考核:将三个项目中做过的问题排查改造成常规监控手段,支持自动的问题发现,告警通知,如有可能,提供自动化或半自动化的问题排查解决方案。

MLOps 系统

MLOps 整体是一个比较大的话题,在这方面有很多产品和系统设计方面的实践可以参考学习。例如 Uber 的 Michelangelo 系列文章,Facebook 的 FBLearner,neptune.ai,dataiku,domino 等,虽然没有开源,但是其背后的很多设计理念,演进思考,白皮书等都非常值得我们学习。在开源界也有很多可以参考的项目,例如 MLflow,Kubeflow,Metaflow,TFX 等,可以学习他们的设计理念,Roadmap,以及实现细节等。

  • 自我考核:总结各个 MLOps 产品的功能模块矩阵对比,能够根据项目需求来进行产品选型与使用。

工程深入方向

数据库

数据库原理

在平时工作中,我们有大量的场景需要用到数据库。从客户数据的对接,数据集的管理和使用,到各种业务系统的数据表设计及优化等,都需要对数据库的运作原理,适用场景,运维使用,性能优化等方面有一定的了解。常见的需要掌握的概念有 OLTP vs OLAP,事务,索引,隔离级别,ACID 与 CAP 理论,数据同步,数据分片,SQL 语法,ORM 等。从底层原理看,会涉及到数据,索引,及日志等存储引擎方面,以及各种计算查询引擎,包括分布式系统的设计与实现。这方面推荐的学习资料有《数据库系统内幕》及《数据密集型应用系统设计》。

  • 自我考核:能够理解 SQL 执行计划,并能够根据执行计划来做索引或查询调优。

关系型数据库

目前常用的关系型数据库主要是 MySQL 和 PostgreSQL,主要需要掌握的是日常的一些 SQL 操作,例如 DML(增删改查),DDL(创建表,修改索引等),DCL(权限相关)。在此基础上还可以进一步了解一些如数据类型,高级计算,存储引擎,部署运维,范式概念与表结构设计等方面的话题。对于高级话题这块,推荐《高性能 MySQL》与《高可用 MySQL》。

  • 自我考核:在 MySQL 中设计相关表结构,存储实际项目中的一系列中间数据集。

NoSQL 数据库

常用的 NoSQL 数据库有几类,KV 存储(Redis),文档数据库(MongoDB),Wide-column 存储(Cassandra,HBase)以及图数据库(Neo4j)。在目前我们的算法项目中,比较有可能会用到的主要是 Redis 这类 KV 存储(也可能把 Cassandra 之类当泛 KV 来用),或者更新一点的类似 Delta Lake 的存储系统。建议学习了解一下这类 KV 存储,以及分布式数据库的常见操作方式,以及基础的运维排查,性能优化方法。

  • 自我考核:考虑一个线上模型服务的场景,用户输入作为基础特征,使用类似 Redis 的 KV 系统,实现实时获取其它特征,并进行模型预测。

云计算

基础架构

IT 系统总体的发展趋势在往云计算方向演进,即使是自建的基础设施,也会采用云计算的一套构建方式,让开发者不用过多的关注底层计算存储资源的部署运维。对于应用开发者来说,需要了解一些基础架构方面的知识,例如各类虚拟化及容器技术,配置管理,容器编排等,便于在日常工作中使用相关技术来管理和发布应用。从工具层面看,Docker 与 k8s 等技术发展速度较快,主要还是根据官方文档来学习为主。浙大之前出版的《Docker – 容器与容器云》一书中有一些更深入的话题的探讨,另外《Kubernetes in Action》中也值得一读。从方法论层面看,《Infrastructure as Code》和《Site Reiliability Engineering》是两本非常不错的学习资料。与算法应用结合的虚拟化,运维,持续集成等都是比较新的领域,需要我们探索出一条可行路线。

  • 自我考核:对于已有的算法项目,总结制定一套开发,测试,发布,运维的标准流程,且尽可能自动化执行。

分布式存储

前些年*流行的分布式存储是脱胎于 Google 经典的 GFS 论文实现的 HDFS,不过随着硬件技术的发展,计算存储分离思想的逐渐兴起,不但灵活性更高,成本更低,且各自架构的复杂度也大大降低了。因此目前更建议学习简单的 object store 形式的分布式存储,例如 s3,minio 等。在此基础上的一些存储系统,例如 Delta Lake,提供了事务,高效的 upsert,time travel 等功能,也值得关注与学习。原理方面,还是推荐《数据密集型应用设计》这本。

  • 自我考核:在项目中实现不同机器能够访问同一个 s3 路径的文件,并进行正常的数据读写,模型文件读写等功能。

分布式计算

大数据时代的分布式计算的鼻祖来自于 Google 经典的 MapReduce 论文,后续在 Hadoop 系统中做了开源实现,在前几年是非常火热的一项技术。目前业界的主流是 Spark 和 Flink,前者在批处理计算中处于霸者地位,后者是流处理领域的*者。目前我们的业务应用中,Spark 是比较常用的分布式计算引擎,其基本操作相关内容比较简单,参考官方文档或者《Spark 快速大数据分析》即可。后续的主要难点会有大数据量下的问题排查与性能调优,执行复杂计算或与 Python 相关 UDF 的交互配合方式等。这方面需要对 Spark 的系统架构,内部原理有一定了解,例如 master,worker,driver,executor 等之间的关系,lazy evaluation,DAG 的 lineage 与 stage 概念,shuffle 优化,wholestage codegen 等技术细节。这方面暂时没有找到比较好的资料,主要还是依赖实际问题解决的经验积累。

  • 自我考核:用 Spark 来实现项目中的特征工程,并在一定数据量情况下取得比单机 Pandas 更好的性能效果。

其它话题

其它云服务基础设施还包括分布式数据库,消息队列,zk/raft 分布式协作系统,虚拟网络,负载均衡等。这些话题离算法应用方面会比较远一些,基本上达到遇到需求时会使用的能力即可,在这里不做展开。

算法深入方向

AutoML

超参优化

自动化机器学习中比较传统的一块是超参数优化,进而可以推广到整个 pipeline 的超参优化,包括数据预处理,特征工程,特征选择,模型选择,模型调优,后处理等部分。目前业界应用比较广泛的技术手段主要是随机搜索,贝叶斯优化,进化算法,Hyperband/BOHB 等,在特征工程方面有 Featuretools,tsfresh,AutoCrossing 等自动化特征工程工具。学术界有一些进一步的探索研究,包括 multi-fidelity 优化,多任务优化,HPO 结合 ensemble learning,pipeline planning,data diff 自动数据分布探测等方面。可以参考 http://automl.org 上的各类参考资料与书籍进行学习了解。主要难点包括 automl 算法的泛化能力,scalability,整体 pipeline 组合的搜索与生成,针对不同学习算法的自动优化手段等。

  • 自我考核:了解超参优化的基础概念,能够在项目中应用框架工具来实现模型超参的贝叶斯优化流程。

元学习

Meta learning 是近年来非常活跃的一个新兴领域,其主要思路是希望能通过元学习模型方法,去积累建模调优的先验知识,跨任务推断模型效果并 warm start 新的训练任务,或者指导学习算法来进行更高效的具体任务的训练过程。这方面在工业界的主要应用基本上集中在建模调优先验知识的积累方面,比如通过一系列公开数据集搜索寻找出表现较好的起始参数,用于指导在新任务上做超参优化的起始搜索点。学术研究中除了 configuration space 的研究,还包括从 learning curve 中进行学习推断,元特征提取与建模,HTN planning 在 pipeline 构建中的应用,以及 MAML 等 few-shot learning 方向的探索。这方面推荐 Lilian Weng 的一系列文章(https://lilianweng.github.io/lil-log/2018/11/30/meta-learning.html),以及 http://automl.org 网站上的资料。

  • 自我考核:设计一系列 meta feature 与 meta learning 手段,实现对新任务的参数选择的初始化。

NAS

AutoML 领域比较火,但也是比较特别的一个方向,目前需要大量的计算资源投入才能做这方面的研究与尝试,因此主要建议了解一下这个方向的一些工作即可,不做深入探索学习。

AutoML 系统

自动化机器学习相关的框架工具也非常多,比较有代表性的框架有 auto-sklearn(来自 http://automl.org 团队),nni(microsoft),auto-gluon(amazon),H2O,ray tune 等,在工具级别也有如 hyperopt,SMAC3,featuretools 等。可以通过学习这些工具框架,了解 AutoML 系统的架构与实现方式,并应用到实际项目中。

  • 自我考核:使用一种 AutoML 系统来进行项目的模型自动优化,并与手工优化的结果进行比较,看是否有所提升,及寻找背后的原因。

模型解释

模型解释技术

主要有三个方面,一是模型本身的解释性,例如线性回归,决策树等,模型结构简单,根据其原理,可以直接对预测结果,特征使用等方面给出解释。另外一些复杂模型,例如 EBM,神经网络,Bayesian rule lists,SLIMs 等,也可以利用一些本身的特性给出一些解释,例如 GradCAM 方法等。二是模型无关的解释方法,包括经典的 PDP,ICE 等特征图,LIME 等 surrogate model 方法,以及基于博弈论的 Shapley 方法。三是基于 sample 的解释方法,例如 conterfactual explanations,adversarial examples,prototypes,influential instances,kNN 等,不过看起来这类方法对于计算的开销一般都会比较大,不太容易在工程中实现落地。这方面的资料可以学习《Interpretable Machine Learning》和《Explainable AI》(关于深度学习的内容会更多)。另外学术界也有很多前沿探索,比如针对模型解释的降维工作,自动的时间序列分析及报告生成,因果模型,模型公平性及社会影响等方面,可以保持关注。

  • 自我考核:理解 LIME,Shapley 的运作原理,并分析其局限性,尝试提出改进方案。

模型解释应用

从工具框架方面,有许多可以使用的开源项目,例如微软的 interpret,eli5,shap,AIX360 等。另外也有一些非传统意义上的模型解释,例如 manifold,tensorboard 这类模型 debugging 工具,自动化的误差分析与模型改进方案,因果模型框架,模型公平性评估与纠正工具等,都可以涵盖在广义的模型解释领域中。在工具基础上,如何结合业务领域知识,给出更有针对性的解释方案,也是值得思考深挖的方向。

  • 自我考核:使用 shap,eli5 等工具来进行模型解释,并在此基础上形成面向开发者的模型 debug,误差分析及改进方案,或形成面向业务的 what-if 分析看板。

总结

目前机器学习应用领域还在高速发展与演进过程中,除了上述提到的技能方向,后续很可能会不断有新的主题引入进来,需要练就快速学习并应用落地的能力。在掌握前面编程,软件工程,机器学习的基础上,后半部分的研究方向,大家可以根据个人兴趣,选择几个进行深入探索与实践。仅阅读相关书籍和文章,只能对知识内容有一个初步的认识,必须要通过深入的动手实践,反复试错思考和修正,才能逐渐内化为自己的技能,并构建起较为坚实的知识体系。

原文链接:https://zhuanlan.zhihu.com/p/192633890

IOS学习之UITableView表视图控件初步

表视图这个控件学习的时候,发现是目前我接触到*复杂的组件。

在Android中也提供了类似表视图的控件叫ListView。

原生的ListView,支持的操作其实很有限,数据的条目展示,点击或是长按的操作。

后来慢慢的衍生出来的索引,分区,动态改变指定条目位置等。

到了IOS发现,原来都是这些设计概念全是从IOS的表视图移植过去的吧。

因此,IOS的表视图是个挺丰富的控件

以下文章内容我基本是这么个流程划分

*简单的表视图——》自定义Cell表——》可编辑表——》可动态移动表

以下是配合Navigation导航条控件演示的tableView各种实现。

一:基础表视图

我们看下表视图一个大致的界面模型

首先是navc的顶级视图

%title插图%num

这个视图控制器的代码基本很前面提到的导航那章一样,只是多了一个数组容器来保存要显示的三个二级视图控制器

看下m文件

[cpp] view plain copy

  1. // 
  2. //  NonoFirstLevelViewController.m 
  3. //  NavTest 
  4. // 
  5. //  Created by Nono on 12-4-26. 
  6. //  Copyright (c) 2012年 NonoWithLilith. All rights reserved. 
  7. // 
  8. #import “NonoFirstLevelViewController.h” 
  9. #import “NonoSecondLevelViewController.h” 
  10. #import “SimpleTableViewController.h” 
  11. #import “CustomCellViewController.h” 
  12. #import “EditViewController.h” 
  13. @interface NonoFirstLevelViewController ()
  14. @end
  15. @implementation NonoFirstLevelViewController
  16. @synthesize controllers = _controllers;
  17. #pragma 实现头文件中自定义方法; 
  18. – (void)initAllSecondControllers:(NSMutableArray *)array
  19. {
  20.     SimpleTableViewController *controller1 = [[SimpleTableViewController alloc] init];
  21.     [controller1 setTitle:@“简单表视图”];
  22.     [array addObject:controller1];
  23.     [controller1 release];
  24.     CustomCellViewController *controller2 = [[CustomCellViewController alloc] init];
  25.      [controller2 setTitle:@“自定义cell视图”];
  26.     [array addObject:controller2];
  27.     [controller2 release];
  28.     EditViewController *controller3 = [[EditViewController alloc] init];
  29.     [controller3 setTitle:@“可编辑视图”];
  30.     [array addObject:controller3];
  31.     [controller3 release];
  32. }
  33. – (id)initWithStyle:(UITableViewStyle)style
  34. {
  35.     self = [super initWithStyle:style];
  36.     if (self) {
  37.     }
  38.     return self;
  39. }
  40. – (void)viewDidLoad
  41. {
  42.     [super viewDidLoad];
  43.     self.title = @“表视图Demo”;
  44.     //实例化一个可变数组 
  45.     NSMutableArray *array = [[NSMutableArray alloc] init ];// 
  46.     self.controllers = array;
  47.     [array release];
  48.     [self initAllSecondControllers:self.controllers];
  49. }
  50. – (void)viewDidUnload
  51. {
  52.     [super viewDidUnload];
  53. }
  54. – (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
  55. {
  56.     return (interfaceOrientation == UIInterfaceOrientationPortrait);
  57. }
  58. #pragma mark – Table view data source 
  59. – (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
  60. {
  61.     return [self.controllers count];
  62. }
  63. – (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  64. {
  65.     static NSString *CellIdentifier = @“FirstLevelCell”;
  66.     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
  67.     if (cell == nil) {
  68.         cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
  69.     }
  70.     NSUInteger row = [indexPath row];
  71.     NonoSecondLevelViewController *controller = [self.controllers objectAtIndex:row];
  72.     cell.textLabel.text = [controller title];
  73.     return cell;
  74. }
  75. #pragma mark – Table view delegate 
  76. – (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
  77. {
  78.     NSUInteger row = [indexPath row];
  79.     NonoSecondLevelViewController  *secondVC = [self.controllers objectAtIndex:row];
  80.      [self.navigationController pushViewController:secondVC animated:YES];
  81. }
  82. @end
  1. //
  2. // NonoFirstLevelViewController.m
  3. // NavTest
  4. //
  5. // Created by Nono on 12-4-26.
  6. // Copyright (c) 2012年 NonoWithLilith. All rights reserved.
  7. //
  8. #import “NonoFirstLevelViewController.h”
  9. #import “NonoSecondLevelViewController.h”
  10. #import “SimpleTableViewController.h”
  11. #import “CustomCellViewController.h”
  12. #import “EditViewController.h”
  13. @interface NonoFirstLevelViewController ()
  14. @end
  15. @implementation NonoFirstLevelViewController
  16. @synthesize controllers = _controllers;
  17. #pragma 实现头文件中自定义方法;
  18. – (void)initAllSecondControllers:(NSMutableArray *)array
  19. {
  20. SimpleTableViewController *controller1 = [[SimpleTableViewController alloc] init];
  21. [controller1 setTitle:@“简单表视图”];
  22. [array addObject:controller1];
  23. [controller1 release];
  24. CustomCellViewController *controller2 = [[CustomCellViewController alloc] init];
  25. [controller2 setTitle:@“自定义cell视图”];
  26. [array addObject:controller2];
  27. [controller2 release];
  28. EditViewController *controller3 = [[EditViewController alloc] init];
  29. [controller3 setTitle:@“可编辑视图”];
  30. [array addObject:controller3];
  31. [controller3 release];
  32. }
  33. – (id)initWithStyle:(UITableViewStyle)style
  34. {
  35. self = [super initWithStyle:style];
  36. if (self) {
  37. }
  38. return self;
  39. }
  40. – (void)viewDidLoad
  41. {
  42. [super viewDidLoad];
  43. self.title = @“表视图Demo”;
  44. //实例化一个可变数组
  45. NSMutableArray *array = [[NSMutableArray alloc] init ];//
  46. self.controllers = array;
  47. [array release];
  48. [self initAllSecondControllers:self.controllers];
  49. }
  50. – (void)viewDidUnload
  51. {
  52. [super viewDidUnload];
  53. }
  54. – (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
  55. {
  56. return (interfaceOrientation == UIInterfaceOrientationPortrait);
  57. }
  58. #pragma mark – Table view data source
  59. – (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
  60. {
  61. return [self.controllers count];
  62. }
  63. – (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  64. {
  65. static NSString *CellIdentifier = @“FirstLevelCell”;
  66. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
  67. if (cell == nil) {
  68. cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
  69. }
  70. NSUInteger row = [indexPath row];
  71. NonoSecondLevelViewController *controller = [self.controllers objectAtIndex:row];
  72. cell.textLabel.text = [controller title];
  73. return cell;
  74. }
  75. #pragma mark – Table view delegate
  76. – (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
  77. {
  78. NSUInteger row = [indexPath row];
  79. NonoSecondLevelViewController *secondVC = [self.controllers objectAtIndex:row];
  80. [self.navigationController pushViewController:secondVC animated:YES];
  81. }
  82. @end

顶视图类基本就是一个导航作用。

线面我么先看*简单的这条目

简单表视图:%title插图%num

[cpp] view plain copy

  1. – (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  2. {
  3.     //控件复用 
  4.     static NSString *CellIdentifier = @“simpleCell”;
  5.     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
  6.     if (cell == nil) {
  7.         cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
  8.     }
  9.     NSUInteger row = [indexPath row];
  10.     NSString *string = [self.data objectAtIndex:row];
  11.     cell.textLabel.text = string;
  12.     //这个可以定义item右端小图标显示风格,默认是none; 
  13.     //cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton; 
  14.     [string release];
  15.     return cell;
  16. }
  1. – (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  2. {
  3. //控件复用
  4. static NSString *CellIdentifier = @“simpleCell”;
  5. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
  6. if (cell == nil) {
  7. cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
  8. }
  9. NSUInteger row = [indexPath row];
  10. NSString *string = [self.data objectAtIndex:row];
  11. cell.textLabel.text = string;
  12. //这个可以定义item右端小图标显示风格,默认是none;
  13. //cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
  14. [string release];
  15. return cell;
  16. }

这边主要说如下几点:

1》。控件得复用,这个和Android很像,因此我们在获取cell对象时,先从原来得复用队列里查找(更具指定的标记,这点也告诉我们,我们可以设置多个标记),

若没有,那就新建一个

2》。整个tableview的style分两种,一种就是顶级视图界面的那种: self.tableView.style = UITableViewStylePlain,另一种就是这个视图的风格:

self.tableView.style = UITableViewStyleGrouped

3》.对于每个item,单元格样式使用了3个不同的单元格元素。依次左边开始有个图标,中间就是一个label,右侧会有一个详情栏。

4》。同样的对于每个cell也是有样式风格的 cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]

针对3,4设置后得某种效果如下:

%title插图%num%title插图%num

左端可以自己敬爱个图标进去,黑体字就是文本label,灰色的是详细文本标签,小箭头图标是accessoryType

以下就是代码

[cpp] view plain copy

  1. static NSString *CellIdentifier = @“FirstLevelCell”;
  2. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
  3. if (cell == nil) {
  4.     cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
  5. }
  6. NSUInteger row = [indexPath row];
  7. NonoSecondLevelViewController *controller = [self.controllers objectAtIndex:row];
  8. cell.textLabel.text = [controller title];
  9. cell.detailTextLabel.text = @“什么情况”;
  10. //这个可以定义item右端小图标显示风格,默认是none; 
  11. cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
  12. return cell;
  1. static NSString *CellIdentifier = @“FirstLevelCell”;
  2. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
  3. if (cell == nil) {
  4. cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier] autorelease];
  5. }
  6. NSUInteger row = [indexPath row];
  7. NonoSecondLevelViewController *controller = [self.controllers objectAtIndex:row];
  8. cell.textLabel.text = [controller title];
  9. cell.detailTextLabel.text = @“什么情况”;
  10. //这个可以定义item右端小图标显示风格,默认是none;
  11. cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton;
  12. return cell;

默认风格的cell是不能显示详情标签内容的。

其实很多效果,代码都走一边就看出来了,具体就自己改动下代码就ok了

二:自定义的Cell

%title插图%num%title插图%num

自定义的cell,xib实现

%title插图%num%title插图%num

基本没什么好说的,看下该类额控制器文件

[cpp] view plain copy

  1. // 
  2. //  CustomCellViewController.m 
  3. //  NavTest 
  4. // 
  5. //  Created by Nono on 12-5-4. 
  6. //  Copyright (c) 2012年 NonoWithLilith. All rights reserved. 
  7. // 
  8. #import “CustomCellViewController.h” 
  9. @interface CustomCellViewController ()
  10. @end
  11. @implementation CustomCellViewController
  12. @synthesize customCell = _customCell;
  13. – (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
  14. {
  15.     self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
  16.     if (self) {
  17.         // Custom initialization 
  18.     }
  19.     return self;
  20. }
  21. – (void)viewDidLoad
  22. {
  23.     [super viewDidLoad];
  24.     NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:@“陈凯”,@“Nono”,@“Lilith”,@“窗前明月光”,@“疑是地上霜”,@“举头望明月”,@“低头思故乡”,@“锄禾日当午”,@“汗滴禾下土”,@“谁知盘中餐”,@“粒粒皆幸苦”,nil];
  25.     self.data = array;
  26.     [array release];
  27.     // Do any additional setup after loading the view from its nib. 
  28. }
  29. – (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
  30. {
  31.     return (interfaceOrientation == UIInterfaceOrientationPortrait);
  32. }
  33. #pragma mark_ 
  34. #pragma 数据源方法 
  35. – (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
  36. {
  37.     return [self.data count];
  38. }
  39. // Row display. Implementers should *always* try to reuse cells by setting each cell’s reuseIdentifier and querying for available reusable cells with dequeueReusableCellWithIdentifier: 
  40. // Cell gets various attributes set automatically based on table (separators) and data source (accessory views, editing controls) 
  41. – (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  42. {
  43.     static NSString *CellIdentifier = @“CustomCell”;
  44.     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
  45.     if (cell == nil) {
  46.         NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@“CustomCell” owner:self options:nil];
  47.         if([nib count] > 0){
  48.             cell = self.customCell;
  49.             cell.backgroundColor = [UIColor redColor];
  50.         }else{
  51.             NSLog(@“加载 nib文件失败”);
  52.         }
  53.     }
  54.     NSUInteger row = [indexPath row];
  55.     NSString *string = [self.data objectAtIndex:row];
  56.     UILabel *customlabel =(UILabel*) [cell viewWithTag:11];
  57.     customlabel.text = string;
  58.     [string release];
  59.     return cell;
  60. }
  61. @end
  1. //
  2. // CustomCellViewController.m
  3. // NavTest
  4. //
  5. // Created by Nono on 12-5-4.
  6. // Copyright (c) 2012年 NonoWithLilith. All rights reserved.
  7. //
  8. #import “CustomCellViewController.h”
  9. @interface CustomCellViewController ()
  10. @end
  11. @implementation CustomCellViewController
  12. @synthesize customCell = _customCell;
  13. – (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
  14. {
  15. self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
  16. if (self) {
  17. // Custom initialization
  18. }
  19. return self;
  20. }
  21. – (void)viewDidLoad
  22. {
  23. [super viewDidLoad];
  24. NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:@“陈凯”,@“Nono”,@“Lilith”,@“窗前明月光”,@“疑是地上霜”,@“举头望明月”,@“低头思故乡”,@“锄禾日当午”,@“汗滴禾下土”,@“谁知盘中餐”,@“粒粒皆幸苦”,nil];
  25. self.data = array;
  26. [array release];
  27. // Do any additional setup after loading the view from its nib.
  28. }
  29. – (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
  30. {
  31. return (interfaceOrientation == UIInterfaceOrientationPortrait);
  32. }
  33. #pragma mark_
  34. #pragma 数据源方法
  35. – (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
  36. {
  37. return [self.data count];
  38. }
  39. // Row display. Implementers should *always* try to reuse cells by setting each cell’s reuseIdentifier and querying for available reusable cells with dequeueReusableCellWithIdentifier:
  40. // Cell gets various attributes set automatically based on table (separators) and data source (accessory views, editing controls)
  41. – (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  42. {
  43. static NSString *CellIdentifier = @“CustomCell”;
  44. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
  45. if (cell == nil) {
  46. NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@“CustomCell” owner:self options:nil];
  47. if([nib count] > 0){
  48. cell = self.customCell;
  49. cell.backgroundColor = [UIColor redColor];
  50. }else{
  51. NSLog(@“加载 nib文件失败”);
  52. }
  53. }
  54. NSUInteger row = [indexPath row];
  55. NSString *string = [self.data objectAtIndex:row];
  56. UILabel *customlabel =(UILabel*) [cell viewWithTag:11];
  57. customlabel.text = string;
  58. [string release];
  59. return cell;
  60. }
  61. @end

提几个注意点:

1》。cell的xib文件得拥有者设置成该类,在该类得头文件中定义一个输出口。

2》 我们看到cell的xib文件有3个label视图我们能看到,其实还有一个没有title的label视图,也就我们要动态添加数据的那个视图,

在xib文件中需要给他设置一个tag,这样我们在代码里才能根据tag找出该对象(和Android中得id很像)。这边我定义了11,所以

 UILabel *customlabel =(UILabel*) [cellviewWithTag:11];

    customlabel.text = string;

3》。xib文件加载,我是根据书上得列子方法。根据应用的束来获取。

4》。哦,还有点就是 static NSString *CellIdentifier = @”CustomCell”;。这个在xib文件得指定器中定义,因为原本我们新建一个cell是有个传入指定标签,
而现在这个新建一个cell说白了就是直接从xib中加载一个实例化了,那么指定器怎需要在xib中定义下。
对于cell简单的自定义就是这样。

三:可编辑的tableView(删除,添加,移动)

%title插图%num%title插图%num

[cpp] view plain copy

  1. // 
  2. //  EditViewController.m 
  3. //  NavTest 
  4. // 
  5. //  Created by Nono on 12-5-4. 
  6. //  Copyright (c) 2012年 NonoWithLilith. All rights reserved. 
  7. // 
  8. #import “EditViewController.h” 
  9. @interface EditViewController ()
  10. @end
  11. @implementation EditViewController
  12. @synthesize edittableView;
  13. – (void)editButtonPressed:(id)sender
  14. {
  15.     [self.edittableView setEditing:!self.edittableView.editing animated:(YES)];
  16.     if (edittableView.editing) {
  17.         [self.navigationItem.rightBarButtonItem setTitle:@“完成”];
  18.     }else {
  19.         [self.navigationItem.rightBarButtonItem setTitle:@“编辑”];
  20.     };
  21.     NSLog(@“点击了按钮”);
  22. }
  23. – (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
  24. {
  25.     self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
  26.     if (self) {
  27.         // Custom initialization 
  28.     }
  29.     return self;
  30. }
  31. – (void)viewDidLoad
  32. {
  33.     [super viewDidLoad];
  34.     NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:@“陈凯”,@“Nono”,@“Lilith”,@“窗前明月光”,@“疑是地上霜”,@“举头望明月”,@“低头思故乡”,@“锄禾日当午”,@“汗滴禾下土”,@“谁知盘中餐”,@“粒粒皆幸苦”,nil];
  35.     self.data = array;
  36.     [array release];
  37.     UIBarButtonItem *rigthButton = [[UIBarButtonItem alloc]  initWithTitle:@“编辑”  style:UIBarButtonItemStyleBordered  target:self  action:@selector(editButtonPressed:)];
  38.     self.navigationItem.rightBarButtonItem = rigthButton;
  39.     //self.navigationItem.prompt = @”加载”; 
  40.     [rigthButton release];
  41.     // Do any additional setup after loading the view from its nib. 
  42. }
  43. – (void)viewDidUnload
  44. {
  45.     [super viewDidUnload];
  46.     // Release any retained subviews of the main view. 
  47.     // e.g. self.myOutlet = nil; 
  48. }
  49. – (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
  50. {
  51.     return (interfaceOrientation == UIInterfaceOrientationPortrait);
  52. }
  53. #pragma mark – Table view data source 
  54. – (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
  55. {
  56.     // Return the number of rows in the section. 
  57.     return [self.data count];
  58. }
  59. – (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  60. {
  61.     static NSString *CellIdentifier = @“editLevelCell”;
  62.     UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
  63.     if (cell == nil) {
  64.         cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
  65.     }
  66.     NSUInteger row = [indexPath row];
  67.     NSString *string = [self.data objectAtIndex:row];
  68.     cell.textLabel.text = string;
  69.     [string release];
  70.     return cell;
  71. }
  72. #pragma 实现数据源协议中一些关于编辑操作方法 
  73. – (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
  74. {
  75.     //是否可以编辑,即是tableView setEditing的前提;默认是yes,实现这个方法估计主要是选择性的编辑条目。 
  76.     return YES;
  77. }
  78. – (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
  79. {
  80.     //同理默认其实就是yes,移动模式(会显示可以触摸得移动button)必须是在实现了下面这个方法才有效,否则及时yes了,移动模式条也是不显示的,简单的说,你不能执行移动操作 
  81.     return YES;
  82. }
  83. //移动操作 
  84. – (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
  85. {
  86.     //拖动得思路就是先备份选中行,删除原来那份,将备份的一份插入到目标行 
  87.     NSUInteger fromRow = [sourceIndexPath row];
  88.     NSUInteger toRow = [destinationIndexPath row];
  89.     id ob = [[self.data objectAtIndex:fromRow] retain];
  90.     [self.data removeObjectAtIndex:fromRow];
  91.     [self.data insertObject:ob atIndex:toRow];
  92.     [ob release];
  93. }
  94. – (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
  95. {
  96.       NSUInteger row = [indexPath row];
  97.     //提交操作完的编辑 
  98.     if (editingStyle == UITableViewCellEditingStyleDelete) {
  99.         [self.data removeObjectAtIndex:row]; //删除操作 
  100.         [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
  101.     }
  102.     if (editingStyle == UITableViewCellEditingStyleInsert) {
  103.         [self.data insertObject:@“插入数据” atIndex:row];//插入操作 
  104.         [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationLeft];
  105.     }
  106. }
  107. #pragma 实现tableView委托中一些方法 
  108. – (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath;
  109. {
  110.     //设置可编辑得样式:系统提供了三种,一种是删除,一种是插入,一种时是none 
  111.     NSInteger row = [indexPath row];
  112.     if(row %2 == 0)//这边做了小处理,间隔显示删除和插入 
  113.     {
  114.         return UITableViewCellEditingStyleDelete;
  115.     }
  116.     return UITableViewCellEditingStyleInsert;
  117. }
  118. @end
  1. //
  2. // EditViewController.m
  3. // NavTest
  4. //
  5. // Created by Nono on 12-5-4.
  6. // Copyright (c) 2012年 NonoWithLilith. All rights reserved.
  7. //
  8. #import “EditViewController.h”
  9. @interface EditViewController ()
  10. @end
  11. @implementation EditViewController
  12. @synthesize edittableView;
  13. – (void)editButtonPressed:(id)sender
  14. {
  15. [self.edittableView setEditing:!self.edittableView.editing animated:(YES)];
  16. if (edittableView.editing) {
  17. [self.navigationItem.rightBarButtonItem setTitle:@“完成”];
  18. }else {
  19. [self.navigationItem.rightBarButtonItem setTitle:@“编辑”];
  20. };
  21. NSLog(@“点击了按钮”);
  22. }
  23. – (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
  24. {
  25. self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
  26. if (self) {
  27. // Custom initialization
  28. }
  29. return self;
  30. }
  31. – (void)viewDidLoad
  32. {
  33. [super viewDidLoad];
  34. NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:@“陈凯”,@“Nono”,@“Lilith”,@“窗前明月光”,@“疑是地上霜”,@“举头望明月”,@“低头思故乡”,@“锄禾日当午”,@“汗滴禾下土”,@“谁知盘中餐”,@“粒粒皆幸苦”,nil];
  35. self.data = array;
  36. [array release];
  37. UIBarButtonItem *rigthButton = [[UIBarButtonItem alloc] initWithTitle:@“编辑” style:UIBarButtonItemStyleBordered target:self action:@selector(editButtonPressed:)];
  38. self.navigationItem.rightBarButtonItem = rigthButton;
  39. //self.navigationItem.prompt = @”加载”;
  40. [rigthButton release];
  41. // Do any additional setup after loading the view from its nib.
  42. }
  43. – (void)viewDidUnload
  44. {
  45. [super viewDidUnload];
  46. // Release any retained subviews of the main view.
  47. // e.g. self.myOutlet = nil;
  48. }
  49. – (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
  50. {
  51. return (interfaceOrientation == UIInterfaceOrientationPortrait);
  52. }
  53. #pragma mark – Table view data source
  54. – (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
  55. {
  56. // Return the number of rows in the section.
  57. return [self.data count];
  58. }
  59. – (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  60. {
  61. static NSString *CellIdentifier = @“editLevelCell”;
  62. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
  63. if (cell == nil) {
  64. cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
  65. }
  66. NSUInteger row = [indexPath row];
  67. NSString *string = [self.data objectAtIndex:row];
  68. cell.textLabel.text = string;
  69. [string release];
  70. return cell;
  71. }
  72. #pragma 实现数据源协议中一些关于编辑操作方法
  73. – (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
  74. {
  75. //是否可以编辑,即是tableView setEditing的前提;默认是yes,实现这个方法估计主要是选择性的编辑条目。
  76. return YES;
  77. }
  78. – (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
  79. {
  80. //同理默认其实就是yes,移动模式(会显示可以触摸得移动button)必须是在实现了下面这个方法才有效,否则及时yes了,移动模式条也是不显示的,简单的说,你不能执行移动操作
  81. return YES;
  82. }
  83. //移动操作
  84. – (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
  85. {
  86. //拖动得思路就是先备份选中行,删除原来那份,将备份的一份插入到目标行
  87. NSUInteger fromRow = [sourceIndexPath row];
  88. NSUInteger toRow = [destinationIndexPath row];
  89. id ob = [[self.data objectAtIndex:fromRow] retain];
  90. [self.data removeObjectAtIndex:fromRow];
  91. [self.data insertObject:ob atIndex:toRow];
  92. [ob release];
  93. }
  94. – (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
  95. {
  96. NSUInteger row = [indexPath row];
  97. //提交操作完的编辑
  98. if (editingStyle == UITableViewCellEditingStyleDelete) {
  99. [self.data removeObjectAtIndex:row]; //删除操作
  100. [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
  101. }
  102. if (editingStyle == UITableViewCellEditingStyleInsert) {
  103. [self.data insertObject:@“插入数据” atIndex:row];//插入操作
  104. [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationLeft];
  105. }
  106. }
  107. #pragma 实现tableView委托中一些方法
  108. – (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath;
  109. {
  110. //设置可编辑得样式:系统提供了三种,一种是删除,一种是插入,一种时是none
  111. NSInteger row = [indexPath row];
  112. if(row %2 == 0)//这边做了小处理,间隔显示删除和插入
  113. {
  114. return UITableViewCellEditingStyleDelete;
  115. }
  116. return UITableViewCellEditingStyleInsert;
  117. }
  118. @end

基本代码如上。

ios 调用webservice整理

学iphone开发有一段时间了,对于我这个新手来说,学习过程中,遇到不少问题,尤其是webservice的调用一直困扰了很久,赶在光棍前夕之迹,谈谈个人在webservice方面遇到的问题以及解决方案~~跟大家分享一下,对于那些高手来说可以略过~~

也不知道这是人生中的第几个光棍节了,没有妹子,很纠结,没有遇到对的人,在爱情的等待中渐渐的发霉~~~~~~~~

在这里也祝单身的男银、女银们,在光棍节脱”光”~~~~废话不多说~~切入正题

一、简述

要调用webservice你想到的是什么?

(1)写一个调用webservice的类(可以自已写,也可以下载第三方的类库)

(2)对webservice返回的xml解析(ios自带的xml解析真的很烦)

      我在网上度娘了很网,都是看到有些人,要么只提供webservice的类,简单的说明一下,对于返回的数据xml,一字不提~~~用起他们写的类来,真的很冒烟啊~~~~,网上找到的一些答案,我觉得很奇怪,大牛们经常会写一句不明不白的话,要写就写清楚,否则干脆不要写~~~让人希望又失望~~我不觉得这样有多牛B。。。反而我更喜欢把复杂的事件简单化~~~

我个人在调用webservice时遇到几个问题:

  a.webservice的命名空间问题?

     webservice默认的命名空间是:http://tempuri.org/

    我刚开始用得好好的,后来报soapAction不识别http://tempuri.org/~~~~那天花了一上午才找出这个错,原来有人改动了webservice的默认命名空间~~~我去你大爷的,改了命名空间,也不通知我,害我瞎折腾~~

  b.xml如何解析问题?

     本来是想用ios自带的类去解析xml的,网上查了一下,看别人写了一大片,脑门一片大汗,满脸黑线~~~,后来我用的是google写的GDataXML这个类库,还不错~~~,不过对于那些xml有命名空间的如何读取,各种坛子去提问,至今还没有人正确解答我~~~难道天朝就没有人知道吗?我失望至*,后来还是有如神助般醍醐灌顶~~~自已解决了,阿门~~~~

  c.异步调用时发生400错误?

    这个问题搞得我,头脑出现乱码,神经也被打茄了,你们猜这是啥原因~~~~

   soap原本要传递的信息是这样:

     <a>XXX</a><b>XXXX</b>

   我把它传递参数颠倒过来了,变成这样:

    <b>XXX</b><a>XXX</a>

    发现问题后,我傻楞般的笑了~~~~

  二、类的简要说明

(1)webservice(一个是使用NSURLConnection写的类,还有一个是ASIHttpRequest写的webservice调用类)

  a.同步调用

  b.异步调用

  c.返回数据的处理

     soap调用返回的数据经常放在:<webservice方法名Result>XXX</webservice方法名Result>中,我在webservice调用中已经直接提取出来了~~~提取出来的内容还是一个xml,这段内容的处理,我单独写在xml解析类里面

(2)xml解析类

1.读取<方法名Result>XXX</方法名Result>的内容

2.遍历xml的所有内容返回数组

三、使用配置

  (1)使用NSURLConnection写的webservice调用类,类名叫:WebServices.h

     包含的文件如下:

    a.AppConfigure.h文件的配置:

  b.google GDataXml配置如下:

     step1.先添加libxml2.dylib类库,不要告诉我这个都不会,拉去面壁~~~

    step2.操作如下图所示

  (2)借助ASIHttpRequest写的webservice调用类,类名叫:ServiceHelper.h

    跟(1)的配置一样,这里就不再重复说明,ASIHttpRequest要多添加几个类库,如下图所示

 四、文件下载

 (1)NSURLConnection类写的webservice调用类的所有文件下载

     http://dl.vmall.com/c0cld6ey99 [注:请下载demo里面的*新版本,有调整]

 (2)ASIHttpRequest类写的webservice调用类的所有文件下载

    http://dl.vmall.com/c0gx4evjva  [注:请下载demo里面的*新版本,有调整]

 五、demo下载      

demo下载地址: http://dl.vmall.com/c016bva6aw

android应用程序开发环境建立

之前一直在linux下面进行开发,用的都是c语言,自己无java基础,心血来潮只想玩玩,无其他追求,现记录当做笔记。

言归正传,要进行Android应用程序开发,首先的建立开发环境,参考网上资料,建立了一个自己的开发环境(vista系统),步骤如下:

1》安装java开发工具包

java开发工具包包括java开发工具JDK(JDK5 或 JDK6)、java运行环境JRE,在我的vista系统中JRE是随机附带的,而且实时升级,所以只需要安装JDK,window下面JDK的安装相当简单,下载到相应的安装文件并双击等待即可(我下载的JDK安装文件为:jdk-6u10-rc2-bin-b32-windows-i586-p-12_sep_2008.exe,可以用google搜索下载)。之后把JDK、JRE相应的bin目录添加到系统环境变量PATH中,这样在dos的命令窗口中,不需要进入到JDK、JRE的bin目录就可以执行相应的操作。

2》下载Eclipse

进入Eclipse的官网:http://www.eclipse.org/downloads/下载集成开发环境Eclipse,下载时请选择“Eclipse IDE for Java Developers”,我下载了安装文件压缩包eclipse-java-helios-win32.zip,把该包放到某个目录下面如E:/java 解压,在E:/java目录下面出现eclipse目录,其中Eclipse的运行文件Eclipse.exe即在目录E:/java/eclipse中,双击Eclipse.exe即可启动集成开发环境eclipse。

3》安装Android开发工具扩展包ADT

启动eclipse,进入“Help->Install New SoftWare”弹出Install对话框,点击“Add…”弹出Add Repository对话框,在Location中输入ADT的网址http://dl-ssl.google.com/android/eclipse/site.xml ,之后点击ok确认,等待在线等待Eclipse搜索合适的ADT版本,之后搜索到该链接下的ADT:“Developer Tools”,选中该组件,确认“Install”按钮。几个下一步之后eclipse会自动安装ADT包,安装完成后eclipse提示重启,按“Yes”键确认重启,即可完成ADT的在线安装。

4》下载设置Android软件开发包SDK

说明下,我在Android官网上未找到合适的SDK版本,google到了一个地址:http://www.163pan.com/files/g0x000p0y.html,进入该URL,点击下载android-sdk-windows-1.5_r3.zip,把该压缩包放入到E:/java解压,在E:/java目录下出现android-sdk-windows目录。

启动eclipse,进入“Windows->Preferences” ,弹出Preferences对话框,在该对话框左边的树形机构中选择Android,点击“Browse…”按钮,选择刚才SDK解压的目录E:/java/android-sdk-windows确认,在Preferences对话框中确认”ok”,这样就可以把软件开发包SDK设置好。

在完成以上步骤后,Android的开发环境就建立好了,可以启动eclipse,进入“File->New->Project…”弹出New Project对话框,观察对话框是否和下图1一致,若能在该对话框中观测到Android 》Android Project则说明Android开发环境建立成功!

%title插图%num

图1 NewProject对话框

之后,可以利用SDK里面的Android应用程式工程实例,启动Android模拟器,如图2所示,具体步骤以后详述。

%title插图%num

图2 Android模拟器

Android模拟器的启动还需要进行一些设定,由于本文的主要内容在于前4步,所以这个过程将不再详述,在以后的文章中,如果涉及到这个问题我将再介绍。

后续:

又参考了网上的其他几篇文章,把SDK升级到了*新版本,模拟器的现实分辨率可以调到854*480,恰好我*近买了moto的milestone,刚好和我的真机匹配!google的东西做的真棒,模拟器做的这么的逼真,真的很方便开发者!我顺便把手机qq也装入到了模拟器中,详细步骤如下:

1》更新SDK

参考了这篇文章:http://www.javaeye.com/topic/520189,首先进入http://androidappdocs.appspot.com/sdk/index.html下载*新的SDK升级引导包,需要注意的是:首先这个包只有24M,不是真正的SDK包,只是一个引导在线升级的包;其次这个包是实时更新的,所以可能你下载的版本和本文中所述的版本不一致,我所下载的版本为:android-sdk_r06-windows.zip。

把包android-sdk_r06-windows.zip解压到E:/java/android-sdk-windows中,启动eclipse,进入“Window->Preferences” ,弹出Preferences对话框,在该对话框左边的树形机构中选择Android,点击“Browse…”按钮,选择刚才SDK解压的目录E:/java/android-sdk-windows确认;进入“Window->Android SDK and AVD Manager”选项卡,在该选项卡的左边选中“Installed Packages” 之后点击选项卡下发的“Update All…”,之后就可以一致等待直到在线升级SDK升级成功。

2》创建模拟器AVD

在SDK包升级成功后,会在E:/java/android-sdk-windows/目录下出现一个tools子目录,该目录下面存放的是android的命令行工具,*好把E:/java/android-sdk-windows/tools加入到环境变量PATH中,方便命令行操作。

通过命令行可创建模拟器AVD:

1:观察模拟器类型

在命令行中输入android list target ,显示:
Available Android targets:
id: 1 or “android-2”
Name: Android 1.1
Type: Platform
API level: 2
Revision: 1
Skins: HVGA (default), HVGA-L, HVGA-P, QVGA-L, QVGA-P
id: 2 or “android-3”
Name: Android 1.5
Type: Platform
API level: 3
Revision: 4
Skins: HVGA (default), HVGA-L, HVGA-P, QVGA-L, QVGA-P
id: 3 or “android-4”
Name: Android 1.6
Type: Platform
API level: 4
Revision: 3
Skins: HVGA (default), QVGA, WVGA800, WVGA854
id: 4 or “android-5”
Name: Android 2.0
Type: Platform
API level: 5
Revision: 1
Skins: HVGA (default), QVGA, WQVGA400, WQVGA432, WVGA800, WVGA854
id: 5 or “android-6”
Name: Android 2.0.1
Type: Platform
API level: 6
Revision: 1
Skins: HVGA (default), QVGA, WQVGA400, WQVGA432, WVGA800, WVGA854
id: 6 or “android-7”
Name: Android 2.1-update1
Type: Platform
API level: 7
Revision: 2
Skins: HVGA (default), QVGA, WQVGA400, WQVGA432, WVGA800, WVGA854
id: 7 or “android-8”
Name: Android 2.2
Type: Platform
API level: 8
Revision: 2
Skins: HVGA (default), QVGA, WQVGA400, WQVGA432, WVGA800, WVGA854

2:创建id为7的模拟器avd

在命令行中输入android create avd -p c:/avd –target 7 –name test -s WVGA854

其中:-p代表模拟器的路径;–target代表模拟器的id;–name代表模拟器的名称;-s代表模拟器的皮肤(分辨率等),至此模拟器创建成功。

3》新建工程

启动eclipse,进入“New->Project…”,弹出“New Project”对话框,在该对好框中选择“Android->Android Project”,点击该选项卡的“Next”按钮,进入Android工程的创建,按照图3所示,使用SDK的exmaple目录下的已经存在的资源来创建工程,其中location中选择E:/java/android-sdk-windows/samples/android-8/ApiDemos,其他项按图3填、选。

%title插图%num

图3 利用SDK开发包中资源创建工程

4》设置工程运行/调试参数

进入“Run->Debug Configurations…”弹出Debug Configurations选项卡,右击该选项卡的左边菜单“Android Application”选择“New”,创建一个新的运行配置文件,各项的填、选如图4所示:

%title插图%num

图4 运行配置设置

5》运行工程

在eclipse的导航窗口中选中工程ApiDemos,进入“Run->Run”,刚开始设置好的avd模拟器就会启动起来,如果5所示。在该过程中,我遇到了一个问题,即模拟器显示的过大,撑满了我的电脑显示器,可以通过一下方法解决:http://android.yaohuiji.com/archives/151。

%title插图%num

图5 android模拟器启动画面

6》安装移动qq到模拟器

1.下载移动qq的安装文件:MobileQQ1_0(Android)_beta2_build0151.apk,可以通过google搜索得到;

2.把上述文件放置到SDK开发包相关位置:E:/java/android-sdk-windows/tools;

3.进入dos工具,cd到E:/java/android-sdk-windows/tools目录;

4.输入命令:adb install MobileQQ1_0(Android)_beta2_build0151.apk,等待片刻即可安装好移动qq。

注:应用程序的安装参考http://www.shouji56.com/ruanjian/article/3121.html。

之后就可以在模拟器的桌面上看到移动qq的快捷方式,点击登录,和我用milestone登录移动qq一样,值得体验!!!如图6所示:

%title插图%num

图6 移动qq快捷方式

后记

通过这一两天的折腾,Android应用程序开发环境算是搭建好了,以后的工作就是学习java,Android应用程序开发了。

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速