1. 程式人生 > >ionic2熱更新外掛cordova-hot-code-push-plugin更新下載檔案

ionic2熱更新外掛cordova-hot-code-push-plugin更新下載檔案

以iOS為例

說下需求:在主專案中根據需要下載子專案(或檔案),並子專案能利用主專案中的cordova-hot-code-push-plugin的跟隨主專案更新。一個是包含全部子專案的頁面,一個是選擇下載子專案後的頁面,都是iOS原生。

一、必要條件

      先把要下載的專案壓縮包放在本地伺服器的 /usr/local/apache-tomcat-7.0.32/webapps/www裡面,以便下載。

2、cordova-hot-code-push-plugin外掛已安裝在專案。

3、本地伺服器已開啟。

二、下載(iOS原生做的下載)

1、首先要先設定好下載路徑,下載路徑要跟熱更新的下載更新路徑是相同的,需要注意一點,檔案不是存放在專案資料夾,是存放在專案執行的路徑

 NSUserDefaults *user=[NSUserDefaults standardUserDefaults];
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSAllLibrariesDirectory, NSUserDomainMask, YES);
    NSLog(@"------------paths=%@",paths);
    
    NSString *documentsDirectory = [NSString stringWithFormat:@"%@/Application Support/com.ionicframework.superappionic558256/cordova-hot-code-push-plugin/%@/www",[paths objectAtIndex:0],[user objectForKey:@"currentReleaseVersionName"]];
看一下拼接路徑:

[paths objectAtIndex:0]:這個不必說,自然是自動獲取;

Application Support:值得一提的是這個資料夾,這個檔案是進入cordova,ionic頁面執行後出現的(個人認為進入cordova就可以有),所以,必須要進入cordova,ionic頁面先執行一次,否則沒有這個資料夾。此時,肯定有人會說,既然這樣那我直接讓做管理子專案下載的頁面控制器繼承於CDVViewController,省了前面的複雜,我利用屬性傳值測試了一下,CDVViewController與UIViewController之間push的時候要麼不傳值,要麼崩,但是,NSUserDefaults是可以的,原因大家自己去找吧;

com.ionicframework.superappionic558256:測試了一下,清空執行快取再執行,兩次生成的都是這個名字資料夾,這跟config.xml有關,由裡面的id決定;

<widget id="com.ionicframework.superappionic558256" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
cordova-hot-code-push-plugin:外掛名;

[user objectForKey:@"currentReleaseVersionName"]:當前顯示給使用者的版本,即chcp.json裡面的release對應的值。去HCPPlugin.m裡面的doLocalInit方法取值

/**
 *  Perform initialization of the plugin variables.執行外掛的初始化
 */
- (void)doLocalInit {
    _defaultCallbackStoredResults = [[NSMutableArray alloc] init];
    
    // init plugin config from xml
    _pluginXmlConfig = [HCPXmlConfig loadFromCordovaConfigXml];
    
    // load plugin internal preferences
    _pluginInternalPrefs = [HCPPluginInternalPreferences loadFromUserDefaults];
    if (_pluginInternalPrefs == nil || _pluginInternalPrefs.currentReleaseVersionName.length == 0) {
        _pluginInternalPrefs = [HCPPluginInternalPreferences defaultConfig];
        [_pluginInternalPrefs saveToUserDefaults];
    }
    
    NSLog(@"Currently running release version %@", _pluginInternalPrefs.currentReleaseVersionName);
    
    //取當前版本號
    NSUserDefaults *user=[NSUserDefaults standardUserDefaults];
    [user setObject:_pluginInternalPrefs.currentReleaseVersionName forKey:@"currentReleaseVersionName"];
    
    // init file structure for www files
    _filesStructure = [[HCPFilesStructure alloc] initWithReleaseVersion:_pluginInternalPrefs.currentReleaseVersionName];
    
    NSLog(@"====%s",__FUNCTION__);
}

www:就是專案的www檔案。

2、下載,ionic2專案建立時就自帶了AFNetworking,所以用它來下載,檔案都是壓縮包Zip的形式,程式碼如下:

 AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    //存放檔案的路徑
    NSString  *fullPath = [NSString stringWithFormat:@"%@/%@.zip", documentsDirectory, projectName];
    NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://192.168.100.234:8080/www/%@.zip",projectName]];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    NSURLSessionDownloadTask *task =
    [manager downloadTaskWithRequest:request
                            progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
                                return [NSURL fileURLWithPath:fullPath];
                            }
                   completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
                       
                   }];
    [task resume];

下載路徑URL是本地伺服器的IP+www+專案名稱.zip,專案壓縮包是提前放在本地伺服器上的。

3、解壓並刪除Zip
 // 解壓
        NSString *zipPath =[NSString stringWithFormat:@"%@/%@.zip",documentsDirectory,projectName];
        NSString *destinationPath = [NSString stringWithFormat:@"%@",documentsDirectory];
        [SSZipArchive unzipFileAtPath:zipPath toDestination:destinationPath];
        
        NSFileManager* fileManager=[NSFileManager defaultManager];
        BOOL blHave=[[NSFileManager defaultManager] fileExistsAtPath:zipPath];
        if (!blHave) {
            NSLog(@"no  have");
            return ;
        }else {
            NSLog(@" have");
            //移除Zip
            BOOL blDele= [fileManager removeItemAtPath:zipPath error:nil];
            if (blDele) {
                NSLog(@"delete success");
            }else {
                NSLog(@"delete fail");
            }
            
        }

解壓用網上的SSZipArchive就可以了。

所以,總結全部,下載功能程式碼如下,

#pragma mark -檔案下載
-(void)downloadFileByAFNetworkingWithProject:(NSString*)projectName documentsDirectory:(NSString*)documentsDirectory{
 
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    //存放檔案的路徑
    NSString  *fullPath = [NSString stringWithFormat:@"%@/%@.zip", documentsDirectory, projectName];
    NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://192.168.100.241:8080/www/%@.zip",projectName]];
    NSURLRequest *request = [NSURLRequest requestWithURL:url];
    NSURLSessionDownloadTask *task =
    [manager downloadTaskWithRequest:request
                            progress:nil destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {
                                return [NSURL fileURLWithPath:fullPath];
                            }
                   completionHandler:^(NSURLResponse *response, NSURL *filePath, NSError *error) {
                       
                   }];
    [task resume];
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        // 解壓
        NSString *zipPath =[NSString stringWithFormat:@"%@/%@.zip",documentsDirectory,projectName];
        NSString *destinationPath = [NSString stringWithFormat:@"%@",documentsDirectory];
        [SSZipArchive unzipFileAtPath:zipPath toDestination:destinationPath];
        
        NSFileManager* fileManager=[NSFileManager defaultManager];
        BOOL blHave=[[NSFileManager defaultManager] fileExistsAtPath:zipPath];
        if (!blHave) {
            NSLog(@"no  have");
            return ;
        }else {
            NSLog(@" have");
            //移除Zip
            BOOL blDele= [fileManager removeItemAtPath:zipPath error:nil];
            if (blDele) {
                NSLog(@"delete success");
            }else {
                NSLog(@"delete fail");
            }
            
        }
    });

}

projectName和documentsDirectory都是在外部呼叫時傳入的,可根據自己的需要調整是否需要傳入。

需要注意一點,本人在呼叫執行的時候,下載速度會慢於解壓,導致解壓時還沒有Zip檔案,所以解壓用dispatch_after做了一個2s的時間延遲,延遲時間可根據情況做設定。

三、更新

1、修改HCPContentManifest.m

官方說法

/**
 *  Model for content manifest.
 *  Content manifest is a configuration file, that holds the list of all web project files with they hashes.內容清單是一個配置檔案,包含所有web專案的列表檔案雜湊表。
 *  Used to determine which files has been removed from the project, which are added or updated.用於確定哪些檔案已經從專案中刪除,新增或更新。
 */

這個HCPContentManifest就是Web內容清單,找到- (HCPManifestDiff *)calculateDifference:(HCPContentManifest *)comparedManifest方法,修改下面這部分
 // find new files
    for (HCPManifestFile *newFile in comparedManifest.files) {
        BOOL isFound = NO;
        for (HCPManifestFile *oldFile in self.files) {
            if ([newFile.name isEqualToString:oldFile.name]) {
                isFound = YES;
                break;
            }
        }
        if (!isFound) {
            [addedFiles addObject:newFile];
        }
    }

修改後,如下
 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
    NSString *newProjName = [ud objectForKey:@"page"];//"proj1","proj2".etc
    NSLog(@"------%s=newProjName=%@",__FUNCTION__,newProjName);
    
    // find new files
    for (HCPManifestFile *newFile in comparedManifest.files) {
        BOOL isFound = NO;
        BOOL isNeed = NO;
        for (HCPManifestFile *oldFile in self.files) {
            if ([newFile.name isEqualToString:oldFile.name]) {
                isFound = YES;
                NSLog(@"++++----- newFile.name ======= %@", newFile.name);
                if (newProjName != nil) {
                    if ([newFile.name hasPrefix: newProjName]) {
                        NSLog(@"+++++++++++ newFile.name必須的 ======= %@", newFile.name);
                        isNeed = YES;
                    }
                }
                break;
            }
        }
        if (!isFound && isNeed) {
            [addedFiles addObject:newFile];
        }
    }

在進入某個子專案時,傳值專案名到這,根據傳值,進入的子專案就是更新後的。

2、修改CDVViewController.m

因為是從iOS原生進入ionic,用瞭如下方法:

 NSString *file=[NSString stringWithFormat:@"%@/index.html",_currentItems[indexPath.item]];
    NSLog(@"--------file==%@",file);
    [user setObject:file forKey:@"page"];
    [user synchronize];
    MainViewController *vc = [[MainViewController alloc] initWithFolderName:@"www" StartPage:file];
    [self.navigationController pushViewController:vc animated:true];

MainViewController是繼承於CDVViewController,iOS原生進入ionic是需要通過cordova,所以修改如下

- (void)loadSettings
{
    CDVConfigParser* delegate = [[CDVConfigParser alloc] init];

    [self parseSettingsWithParser:delegate];

    // Get the plugin dictionary, whitelist and settings from the delegate.
    self.pluginsMap = delegate.pluginsDict;
    self.startupPluginNames = delegate.startupPluginNames;
    self.settings = delegate.settings;

    // And the start folder/page.
    if(self.wwwFolderName == nil){
        self.wwwFolderName = @"www";
    }
//    if(delegate.startPage && self.startPage == nil){
        //self.startPage = delegate.startPage;
//    }
    NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
    self.startPage = [ud objectForKey:@"page"];
        NSLog(@"-------cdv-startPage==%@",self.startPage);

    // Initialize the plugin objects dict.
    self.pluginObjects = [[NSMutableDictionary alloc] initWithCapacity:20];
}

代理這部分註釋掉,是因為會預設執行代理的,即Staging檔案裡面的config.xml的<content src="index.html" />,所以把self.startPage改成靈活的,可根據自己的選擇進入想要進入的頁面。

3、修改HCPPlugin.m

把indexPageFromConfigXml方法裡面的“_indexPage = DEFAULT_STARTING_PAGE;”改成如下:

 NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
        NSString *startUrl=[ud objectForKey:@"page"];
        _indexPage = startUrl;//DEFAULT_STARTING_PAGE;

做這個操作是因為:在更新後,第一次進入頁面會進入預設的www/index.html,退出後,再進入,才是更新後的,為了解決這個問題而做的修改。

4、更新步驟

  前提:帶有子專案的主專案在執行中。

  子專案更新:

       ①、在子專案中需要的地方做修改;

       ②、ionic serve;

       ③、cordova-hcp build;

       ④、ionic build iOS;

       ⑤、將子專案platforms--iOS中的www複製,放在主專案外部的www檔案;

       ⑥、主專案cordova-hcp build;

       ⑦、將主專案外部的www檔案複製,放到伺服器 /usr/local/apache-tomcat-7.0.32/webapps,覆蓋原來的www檔案,如有其它需要下載的子專案,則放入新www中。

  主專案更新:

      ①、在主專案中需要的地方做修改;

      ②、ionic serve;

      ③、cordova-hcp build;

      ⑦、將主專案外部的www檔案複製,放到伺服器 /usr/local/apache-tomcat-7.0.32/webapps,覆蓋原來的www檔案。

以上就是主要功能,還有許多細節需要自己完善,裡面很多操作是靈活,根據自己的需要用合適的方法就可以了,比如傳值方式。

這裡iOS和Android很多方法都是通用的。

(網不好,沒上傳圖片。如果有更好的建議和方法可以留言啊!!)