Android实现app内部自动检测版本更新、自动安装及数据库更新升级

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/Nirvana_lss/article/details/89879571

Android实现app内部自动检测版本更新、自动安装及数据库更新升级

    1、apk更新流程
    2、获取本地app内部版本工具类
    3、请求服务器获取版本数据及新旧版本比对
    4、下载最新版本apk
    5、监听下载是否完成并自动安装
    6、在AndroidManifest.xml开权限并配置receiver
    7、弹出框布局Dialog
    8、自定义Dialog
    9、使用xutils3升级最新版本数据库
    10、效果图
    11、总结

因为有的app应用不需要上线在应用市场,只需要在内部使用,所以就需要实现app内部检测最新版本功能,最近正好做了这个功能,所以把它分享出来:

1、apk更新流程

登录成功后请求服务器接口获取最新版本及更新内容(请求使用的xutils3框架)
获取本地app应用内版本versionCode与最新版本比较,若小于最新版本则弹框提示更新
更新下载apk并自动安装,进入主页面删除旧的apk包
程序启动初始化升级本地数据库

以上便是全部流程,步骤很简单,接下来就是代码实现
2、获取本地app内部版本工具类

package com.gtlxkj.cn.util;

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;
        }
    }

    /**
     * 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 "";
    }
 
}

3、请求服务器获取版本数据及新旧版本比对

 /**
 * 主页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;//是否检测版本初始化

    @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"); 
			}
    	});
    }
}

后台用的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" >

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);
    }

此处使用DownloadManager 下载apk
5、监听下载是否完成并自动安装

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();
        }
 
    }
    
}

6、在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" />
receiver:

  <receiver
            android:name="com.gtlxkj.cn.activity.InstallReceiver">
            <intent-filter>
                <action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
            </intent-filter>
        </receiver>

7、弹出框布局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>

8、自定义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();
		}
    }
}

9、使用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();
        }
    }
}

10、效果图

demo
11、总结

数据库升级在此文中没有过多的说明,有需要的可以百度,有关资料还是很多的,也比较简单,打好的apk包放到服务器上,安卓请求这个apk链接就可以下载,版本升级切记打包的时候要使配置的版本和服务器接口内的版本一致同步,
————————————————
版权声明:本文为CSDN博主「Nirvana_lss」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Nirvana_lss/arti

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Android应用程序的自动更新可以通过以下步骤实现: 1. 在应用程序添加版本检查功能,以检查当前应用程序版本是否为最新版本。 2. 如果当前应用程序版本不是最新版本,则从服务器下载最新版本的应用程序。 3. 下载完成后,使用Android的PackageInstaller API安装版本的应用程序。 4. 在应用程序添加后台服务,以定期检查版本的应用程序是否可用,并在发现新版本自动下载和安装。 5. 在应用程序添加设置选项,以允许用户选择是否启用自动更新功能,并设置更新检查的时间间隔。 6. 在应用程序添加通知功能,以通知用户新版本的应用程序已经下载并准备安装。 以上是Android应用程序自动更新的基本实现步骤。需要注意的是,在实现自动更新功能时,需要确保应用程序的安全性和稳定性,并遵循Google Play Store的开发者政策和规定。 ### 回答2: Android应用自动更新功能对于用户来说是非常方便的,因为可以自动升级应用程序并不需要用户手动下载或更新应用。从开发人员的角度来看,自动更新也可以提高应用程序的可靠性和安全性。 实现Android应用的自动更新,一般需要应用程序具有以下特点: 1.应用程序需要有版本号:在应用程序设置版本号,可以让应用程序知道当前的版本,从而实现更新。 2.应用程序需要能够获取服务器的最新版本号:一般是通过网络请求(HTTP请求接口)获取服务器的最新版本号。 3.应用程序需要将最新版本号与当前版本号进行比较:如果最新版本号大于当前版本号,则需要进行更新。 4.应用程序需要自动下载最新版本的APK文件:一般是通过下载管理器进行下载。 5.安装最新版本:通过调用系统的安装器来安装最新版本的APK文件。 在实现自动更新的过程,需要做好以下几方面的工作: 1.确定应用程序的版本号,并与服务器上的版本号保持同步。 2.建立服务器端接口,提供应用程序的版本信息,以供客户端获取。 3.客户端获取最新版本信息后进行比较,根据比较结果决定是否需要更新。 4.下载最新版本的APK文件,并确保下载过程不出现错误。 5.执行APK文件安装,并确保在安装过程不出现错误。 总的来说,实现Android应用的自动更新需要对Android开发框架、网络通信、下载、存储、安装等方面有比较深入的了解,需要开发人员具备一定的技术实力。如果需要实现这个功能,可以参考Android开发文档、开源框架或网上的相关教程,以实现自动更新的功能。 ### 回答3: Android应用的自动更新是一种很重要的功能,可以方便地向用户推送新版本,并解决了用户手动更新应用的繁琐问题。下面将介绍如何实现Android app自动更新。 1. 获取新版本信息 首先,我们需要在应用服务器上存储最新版本的信息,包括版本号、版本名、更新说明、下载链接等。当用户打开应用时,应用可以向服务器请求最新版本的信息。如果服务器返回的版本号大于当前应用版本号,就说明有新版本,需要更新。否则,应用继续运行。 2. 下载新版本apk 获取新版本信息后,应用需要下载最新版本的apk文件。一般情况下,我们会把apk文件上传到服务器,并返回下载链接。应用可以通过建立HTTP连接来下载apk文件,并保存到本地存储器。下载完毕后,应用需要获取文件头,获取apk文件的版本号和包名。 3. 安装版本apk 下载完成后,应用需要自动打开安装新版应用。由于apk文件需要具有写操作权限才能被安装,因此需要在AndroidManifest.xml文件添加读写文件权限。安装时需要调用系统提供的安装接口来完成。调用语句如下: Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(Uri.fromFile(f), "application/vnd.android.package-archive"); startActivity(intent); 其,f是apk文件的File对象。开始安装后,系统会提示用户是否允许应用获取读写权限,如果用户没有允许,安装过程会断。 4. 其他实现细节 为避免重复下载,可以在服务器端设定最新版本的HASH值,app检查是否有新版本的时候通过HASH检查是否更新。 新版本需要提醒用户更改了什么,这就需要app在服务器端声明release notes,app通过检查版本的主要变化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值