Android實現app內部自動檢測版本更新、自動安裝及數據庫更新升級

Android實現app內部自動檢測版本更新、自動安裝及數據庫更新升級
一、apk更新流程
二、獲取本地app內部版本工具類
三、請求服務器獲取版本數據及新舊版本比對
四、下載最新版本apk
五、監聽下載是否完成並自動安裝
六、在AndroidManifest.xml開權限並配置receiver
七、彈出框佈局Dialog
八、自定義Dialog
九、使用xutils3升級最新版本數據庫
十、效果圖
十一、總結java

由於有的app應用不須要上線在應用市場,只須要在內部使用,因此就須要實現app內部檢測最新版本功能,最近正好作了這個功能,因此把它分享出來:
一、apk更新流程
登陸成功後請求服務器接口獲取最新版本及更新內容(請求使用的xutils3框架)
獲取本地app應用內版本versionCode與最新版本比較,若小於最新版本則彈框提示更新
更新下載apk並自動安裝,進入主頁面刪除舊的apk包
程序啓動初始化升級本地數據庫
以上即是所有流程,步驟很簡單,接下來就是代碼實現android

二、獲取本地app內部版本工具類
package com.gtlxkj.cn.util;spring

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Environment;數據庫

public class VersionUtils {
    /**
     * 檢查是否存在SDCard
     *
     * @return
     */
    public static boolean hasSdcard() {
        String state = Environment.getExternalStorageState();
        if (state.equals(Environment.MEDIA_MOUNTED)) {
            return true;
        } else {
            return false;
        }
    }springboot

    /**
     * 2 * 獲取版本
     */
    public static int getVersion(Context context) {
        try {
            PackageManager manager = context.getPackageManager();
            PackageInfo info = manager.getPackageInfo(context.getPackageName(),0);
            int versioncode = info.versionCode;
            return versioncode;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }
    
    /**
     * 2 * 獲取版本名稱
     */
    public static String getVersionName(Context context) {
        try {
            PackageManager manager = context.getPackageManager();
            PackageInfo info = manager.getPackageInfo(context.getPackageName(),0);
            String version = info.versionName;
            return version;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }
 
}服務器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
三、請求服務器獲取版本數據及新舊版本比對
 /**
 * 主頁Activity
 * 
 */
@ContentView(R.layout.activity_home)
public class HomeActivity extends BaseActivity implements View.OnClickListener {網絡

    @ViewInject(R.id.btn_upload)
    private Button btn_upload;
    @ViewInject(R.id.update_apk)
    private TextView update_apk;
    private Context context = HomeActivity.this;
    private VersionDialog dialog;
    private boolean flag;//是否檢測版本初始化app

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i("HomeActivity", "onCreate"); 
      
       initListener();
       
       Utils.deleteLocal(new File(ConfigurationUtil.APK_PATH_ABSOULT+"GTLXKJ.apk"));//刪除舊的apk
       flag=true;
       // 獲取本版本號,是否更新
       getVersion(VersionUtils.getVersion(this));
    }框架

    //初始點擊事件
    private void initListener(){
        Log.i("HomeActivity", "initListener"); 
        update_apk.setOnClickListener(this);
        update_apk.setText("版本號 V"+Tools.getVersionName(this));
    }
    
    /**
     * 點擊事件
     * @param view
     */
    public void onClick(View v) {
        Log.i("HomeActivity", "onClick"); 
        switch(v.getId()){
            case R.id.update_apk:
                flag=false;
                getVersion(Tools.getVersion(this));
                break;    
        }
    }
    
   
    
  //版本更新彈出框
  public  void showUploadApkDialog(String content,String versionName) {
        Log.i("HomeActivity", "showUploadTaskDialog"); 
        dialog = new VersionDialog(this,R.style.mystyle,this,R.layout.version__update_dialog,content,versionName);
        dialog.show();
   }
    
    
    
    // 獲取更新版本號
    private void getVersion(final int vision) {
        if (!NetWorkUtil.isNetworkAvalible(context)) {
            Toast.makeText(HomeActivity.this,"請檢查網絡",Toast.LENGTH_SHORT).show();
            return;
        }
        
         RequestParams params = new RequestParams(ConfigurationUtil.WEBVERSION);
         x.http().get(params, new Callback.ProgressCallback<String>() {
            @Override
            public void onSuccess(String result) {
                Log.i("onSuccess", "onSuccess"); 
                try{
                       JSONObject object = new JSONObject(result);
                       //返回狀態
                       String status = object.optString("code");
                       //返回的更新內容
                      String content = object.optString("content");
                      //返回的版本號
                      String versionName = object.optString("msg");
                      //返回版本
                       String nversion = object.optString("data");
                       if("1".equals(status)){
                           int newversion = Integer.parseInt(nversion);
                           if (newversion != vision) {//新舊版本比較
                               if (vision < newversion) {//舊版本低於新版本則更新
                                   System.out.println("新版本:v"+newversion + "   舊版本v"+ vision);
                                   // 版本號不一樣
                                   showUploadApkDialog(content,versionName);
                               }
                           }else if(!flag){
                               Toast.makeText(HomeActivity.this,"已是最新版本",Toast.LENGTH_SHORT).show();
                          }
                       }
                }catch(Exception e){
                    e.printStackTrace();
                    Log.e("onSuccess", "Error"); 
                }
            }
            @Override
            public void onCancelled(CancelledException arg0) {
                Log.i("onCancelled", "onCancelled"); 
            }
            @Override
            public void onError(Throwable arg0, boolean arg1) {
                Log.e("onError", "onError"); 
                 Toast.makeText(HomeActivity.this,"請求服務器無響應",Toast.LENGTH_SHORT).show();
            }
            @Override
            public void onFinished() {
                Log.i("onFinished", "onFinished"); 
            }
            @Override
            public void onLoading(long arg0, long arg1, boolean arg2) {
                Log.i("onLoading", "onLoading"); 
            }
            @Override
            public void onStarted() {
                Log.i("onStarted", "onStarted"); 
            }
            @Override
            public void onWaiting() {
                Log.i("onWaiting", "onWaiting"); 
            }
        });
    }
}ide

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
後臺用的springboot,具體接口比較簡單就不展現了

AndroidManifest.xml文件中這裏配置版本號,發佈新版本apk到服務器上時不要忘了修改這裏的版本號

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.gtlxkj.cn"
    android:versionCode="5"
    android:versionName="5.0.0" >
1
2
3
4
四、下載最新版本apk
    //下載apk
    public void downloadApk(String apkUrl) throws PackageManager.NameNotFoundException {
        Utils.deleteLocal(new File(ConfigurationUtil.APK_PATH_ABSOULT+"GTLXKJ.apk"));//刪除舊的apk
        Uri uri = Uri.parse(apkUrl);
        DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
        DownloadManager.Request request = new DownloadManager.Request(uri);
        // 設置容許使用的網絡類型,這裏是移動網絡和wifi均可以
        request.setAllowedNetworkTypes(request.NETWORK_MOBILE | request.NETWORK_WIFI);
        //設置是否容許漫遊
        request.setAllowedOverRoaming(true);
        //設置文件類型
        MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
        String mimeString = mimeTypeMap.getMimeTypeFromExtension(MimeTypeMap.getFileExtensionFromUrl(apkUrl));
        request.setMimeType(mimeString);
        //在通知欄中顯示
        request.setNotificationVisibility(request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
        request.setTitle("正在下載");
        request.setVisibleInDownloadsUi(true);
        //sdcard目錄下的download文件夾
        request.setDestinationInExternalPublicDir(ConfigurationUtil.APK_PATH, "GTLXKJ.apk");
        // 將下載請求放入隊列
        downloadManager.enqueue(request);
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
此處使用DownloadManager 下載apk

五、監聽下載是否完成並自動安裝
package com.gtlxkj.cn.activity;

import java.io.File;

import com.gtlxkj.cn.util.ConfigurationUtil;
import com.gtlxkj.cn.util.Utils;

import android.app.DownloadManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;

public class InstallReceiver extends BroadcastReceiver {
     
    // 安裝下載接收器
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
            installApk(context);
        }
  }
 
    // 安裝Apk
    private void installApk(Context context) {
        try {
            Intent i = new Intent(Intent.ACTION_VIEW);
            String filePath = ConfigurationUtil.APK_PATH_ABSOULT+"GTLXKJ.apk";
            System.out.println(filePath);
            i.setDataAndType(Uri.parse("file://" + filePath), "application/vnd.android.package-archive");
            i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(i);
        } catch (Exception e) {
            e.printStackTrace();
        }
 
    }
    
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
六、在AndroidManifest.xml開權限並配置receiver
權限:

  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

1
2
3
4
receiver:

  <receiver
            android:name="com.gtlxkj.cn.activity.InstallReceiver">
            <intent-filter>
                <action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
            </intent-filter>
        </receiver>
1
2
3
4
5
6
七、彈出框佈局Dialog
<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="300dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:background="@drawable/dialog4" >
 
    <LinearLayout
        android:id="@+id/lay_finish"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:orientation="vertical"
        android:scrollbars="vertical" >
 
        <TextView
            android:id="@+id/lay_view"
            android:layout_width="fill_parent"
            android:layout_height="50dp"
            android:gravity="center"
            android:text="版本更新"
            android:textColor="#5b5d61"
            android:textSize="18sp" />
 
        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="1dip"
            android:background="#D1D1D1" />
 
       <ScrollView
        android:id="@+id/lay_work"
        android:layout_width="match_parent"
        android:layout_height="250dp"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="10dp"
        android:orientation="vertical"
        android:scrollbars="none">
 
        <TextView
            android:id="@+id/version_content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="8dp"
            android:layout_marginRight="8dp"
            android:textColor="#5b5d61"
            android:textSize="16sp" />
         </ScrollView>
        
         <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="1dip"
            android:background="#D1D1D1" />
       
        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="40dp"
            android:orientation="horizontal" >
 
            <Button
                android:id="@+id/cancal"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                android:layout_weight="1"
                android:background="@null"
                android:text="稍後"
                android:textColor="#91c11e"
                android:textSize="15dp" />
 
            <LinearLayout
                android:layout_width="1dp"
                android:layout_height="fill_parent"
                android:background="#D1D1D1" />
 
            <Button
                android:id="@+id/update"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                android:layout_weight="1"
                android:background="@null"
                android:text="更新"
                android:textColor="#91c11e"
                android:textSize="15dp" />
        </LinearLayout>
    </LinearLayout>
 
</LinearLayout>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
八、自定義Dialog
package com.gtlxkj.cn.dialog;

import com.gtlxkj.cn.R;
import com.gtlxkj.cn.activity.HomeActivity;
import com.gtlxkj.cn.util.ConfigurationUtil;

import android.app.Dialog;
import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

/**
 * 自定義dialog
 */
public class VersionDialog extends Dialog implements
        View.OnClickListener {

    /**
     * 佈局文件
     **/
    int layoutRes;

    /**
     * 上下文對象
     **/
    Context context;
    

    /**
     * 取消按鈕
     **/
    private Button bt_cancal;

    /**
     * 更新按鈕
     **/
    private Button bt_delect;
    
    private String content;//版本內容
    
    private String versionName;//版本號
    
    private HomeActivity homeActivity;
    
    
    public VersionDialog(Context context) {
        super(context);
        this.context = context;
    }

    /**
     * 自定義佈局的構造方法
     *
     * @param context
     * @param resLayout
     */
    public VersionDialog(Context context, int resLayout) {
        super(context);
        this.context = context;
        this.layoutRes = resLayout;
    }

    /**
     * 自定義主題及佈局的構造方法
     *
     * @param context
     * @param theme
     * @param resLayout
     * @param postion
     */
    public VersionDialog(Context context,int theme, HomeActivity activity,int resLayout,String content,String versionName) {
        super(context, theme);
        this.context = context;
        this.content = content;
        this.layoutRes = resLayout;
        this.homeActivity = activity;
        this.versionName = versionName;
    }

    

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // 指定佈局
        this.setContentView(layoutRes);
        TextView  textView=(TextView) findViewById(R.id.version_content);
        TextView  titleView=(TextView) findViewById(R.id.lay_view);
        textView.setText("更新內容:\n\n"+content);
        titleView.setText("發現新版本 "+versionName);
        // 根據id在佈局中找到控件對象
        bt_cancal = (Button) findViewById(R.id.cancal);
        bt_delect = (Button) findViewById(R.id.update);
        // 爲按鈕綁定點擊事件監聽器
        bt_cancal.setOnClickListener(this);
        bt_delect.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {

            case R.id.update:
                optUpdateApk();
                this.dismiss();
                break;

            // 取消按鈕
            case R.id.cancal:
                this.dismiss();

            default:
                break;
        }
    }
    /**
     * 操做  版本更新
     *
     */
    private void optUpdateApk( ) {
        try {
            homeActivity.downloadApk(ConfigurationUtil.APKHOST);
        } catch (NameNotFoundException e) {
            e.printStackTrace();
        }
    }
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
九、使用xutils3升級最新版本數據庫
注意此升級代碼是在onUpgrade方法內實現,
這樣更新完app後從新啓動就會初始化升級本地數據庫結構了

public class LXApplication extends Application {
    
    // Effective Java 初版推薦寫法
    private static class LXApplicationHolder {
        private static final LXApplication INSTANCE = new LXApplication();
    }
    
    public LXApplication() {
    }

    public static final LXApplication getInstance() {
        return LXApplicationHolder.INSTANCE;
    }

    public static DbManager.DaoConfig daoConfig;

    @Override
    public void onCreate() {
        super.onCreate();
        CrashHandler crashHandler = CrashHandler.getInstance();
        crashHandler.init(this);
        x.Ext.init(this);
        x.Ext.setDebug(true);
        dbinit();
    }
    //xutils3初始化
    private void dbinit() {
        File file = new File(ConfigurationUtil.SDFILE + "db");
        if (!file.exists()) {
            file.mkdirs();
        }
        daoConfig = new DbManager.DaoConfig()
                .setDbName(ConfigurationUtil.DBNAME)
                .setDbDir(file).setDbVersion(ConfigurationUtil.DBVERSION)
                .setAllowTransaction(true)
                .setDbOpenListener(new DbManager.DbOpenListener() {
            @Override
            public void onDbOpened(DbManager db) {
                // 開啓WAL, 對寫入加速提高巨大
                db.getDatabase().enableWriteAheadLogging();
            }
        }).setDbUpgradeListener(new DbManager.DbUpgradeListener() {
            @Override
            public void onUpgrade(DbManager db, int oldVersion, int newVersion) {//數據庫升級
                try {
                     for (int j = oldVersion; j <= newVersion; j++) {
                        switch(newVersion){
                            case 2:
                                //增長表字段
                                db.addColumn(Person.class, "name");
                               break;
                             case 3:
                               /**
                                *保留歷史數據的升級數據庫
                                */

                               // db.execSQL(""); //第一步將舊錶改成臨時表
        
                               //db.execSQL(""); //第二步建立新表(新添加的字段或去掉 的字段)
        
                               //db.execSQL(""); //第三步將舊錶中的原始數據保存到新表中以防遺失
        
                               //db.execSQL(""); //第四步刪除臨時備份表
                               break;
                            case 4://其餘操做
                                // TODO: ...
                                // db.dropTable(...);
                                // ...
                                // or
                                // db.dropDb();
                                break;
                            default:
                                break;
                         }
                     }
                } catch (DbException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                
            }
        }).setTableCreateListener(new DbManager.TableCreateListener() {// 設置表建立的監聽

            @Override
            public void onTableCreated(DbManager arg0, TableEntity<?> arg1) {
                Log.i("sjl__", "onTableCreated:" + arg1.getName());
            }
        });
    }

    
    public DbManager.DaoConfig getDbConfig() {
        if (daoConfig == null) {
            dbinit();
        }
        return daoConfig;
    }

    private static List<Activity> activityLists = new ArrayList<Activity>();

    // 添加Activity到容器中
    public static void addActivity(Activity activity) {
        activityLists.add(activity);
    }

    // 遍歷全部Activity並finish
    public static void exit() {
        try {
            for (Activity activity : activityLists) {
                activity.finish();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
十、效果圖

十一、總結 數據庫升級在此文中沒有過多的說明,有須要的能夠百度,有關資料仍是不少的,也比較簡單,打好的apk包放到服務器上,安卓請求這個apk連接就能夠下載,版本升級切記打包的時候要使配置的版本和服務器接口內的版本一致同步, ———————————————— 版權聲明:本文爲CSDN博主「Nirvana_lss」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連接及本聲明。 原文連接:https://blog.csdn.net/Nirvana_lss/article/details/89879571

相關文章
相關標籤/搜索