Android App更新模块

  • 应用更新有两种:
    1、 检测到更新,App实现下载功能,下载安装。
    2、检查到更新,跳转应用市场下载。

  • 两种方法优缺点:
    第一种:

  • 优点:是,下载统一,便于版本统一升级,只要用户点击更新,可以保证其实官方最新的包。

  • 缺点:实现起来稍微优点麻烦,如果写不好,下载的包可能无法解析,有些机型导致下载了无法安装的问题。

    第二种:

    • 优点:实现简单,几乎不用写几行代码。
    • 缺点:每个手机安装的应用市场不同,不同应用市场的审核速度不一样,导致用户用的版本参差不齐,提示更新后,用户去应用市场下载安装,发现还提示更新,是因为应用市场上不是最新包。

下面先给出第二种实习方法,再给第一种方法(实践后结论下载稳定)

//检查到需要更新,调用
 ToolUtil.gotoMarket(mActivity, mActivity.getPackageName());

 /**
     * 打开市场
     */
    public static void gotoMarket(Context context, String pck) {
        if (!isHaveMarket(context)) {
            Toast.makeText(context, "您手机中没有安装应用市场!", Toast.LENGTH_SHORT).show();
            return;
        }
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_VIEW);
        intent.setData(Uri.parse("market://details?id=" + pck));
        if (intent.resolveActivity(context.getPackageManager()) != null) {
            context.startActivity(intent);
        }
    }

 /**
     * 是否安装市场
     */
    private static boolean isHaveMarket(Context context) {
        Intent intent = new Intent();
        intent.setAction("android.intent.action.MAIN");
        intent.addCategory("android.intent.category.APP_MARKET");
        PackageManager pm = context.getPackageManager();
        List<ResolveInfo> infos = pm.queryIntentActivities(intent, 0);
        return infos.size() > 0;
    }

第二种到此就完了。可以验证下,前题是,你的app要在应用商场上线。没有上线的话,是找不到对应的APP的。



那么下来重点说下第一种:

先看效果图:

下载效果图

这里先说,图中的dialog,用的是github上 material-dialogs 。(这了忽略对话框的代码)

从左到右,检查更新-->是否需要下载-->点击下载-->状态栏和对话框同时走下载进度条-->最后下载成功。

  • 提出下载的主要代码。

// 启动Service 开始下载
AppUpdateService.startUpdate(mContext, url, fileName, new AppUpdateService.OnProgressListener() {
            @Override
            public void onProgress(int progress) {
                //更新对话框进度条
                mDialog.setProgress(progress);
            }

            @Override
            public void onSuccess(boolean isSuccess) {
                mDialog.dismiss();
                //失败提示
                if (!isSuccess) {
                    ToastTools.toastLong("更新不成功");
                }
            }
        });

这里用到的InternetService类,IntentService是Service的子类,
IntentService实现了work非阻塞线程,适合做耗时操作,可以自行Google or 百度 他们的区别和用法。

package com.hundun.yanxishe.service;

import android.app.IntentService;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.widget.RemoteViews;

import com.hundun.yanxishe.R;
import com.hundun.yanxishe.tools.LogTool;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * An {@link IntentService} subclass for handling asynchronous task requests in
 * a service on a separate handler thread.
 * <p>
 * helper methods.
 */
public class AppUpdateService extends IntentService {
    private static final String ACTION_UPDATE = "com.hundun.yanxishe.service.action.update";
    private static final String EXTRA_URL = "com.hundun.yanxishe.service.extra.url";
    private static final String EXTRA_FILE_NAME = "com.hundun.yanxishe.service.extra.file.name";
    private boolean isRunning = false;
    private NotificationManager updateNotificationManager;
    private Notification updateNotification;
    private PendingIntent updatePendingIntent;
    private static OnProgressListener mProgressListener;

    public interface OnProgressListener {
        void onProgress(int progress);

        void onSuccess(boolean isSuccess);
    }

    public AppUpdateService() {
        super("AppUpdateService");
    }


    @Override
    protected void onHandleIntent(Intent intent) {
        if (intent != null) {
            final String action = intent.getAction();
            if (ACTION_UPDATE.equals(action)) {
                final String param1 = intent.getStringExtra(EXTRA_URL);
                final String param2 = intent.getStringExtra(EXTRA_FILE_NAME);
                startDownloade(param1, param2);
            }
        }
    }

    @Override
    public void onDestroy() {
        mProgressListener = null;
        super.onDestroy();
    }

    /**
     * Starts this service to perform action Baz with the given parameters. If
     * the service is already performing a task this action will be queued.
     *
     * @see IntentService
     */
    public static void startUpdate(Context context, String param1, String param2, OnProgressListener pregressListener) {
        mProgressListener = pregressListener;
        Intent intent = new Intent(context, AppUpdateService.class);
        intent.setAction(ACTION_UPDATE);
        intent.putExtra(EXTRA_URL, param1);
        intent.putExtra(EXTRA_FILE_NAME, param2);
        context.startService(intent);
    }

    private void startDownloade(String url, String fileName) {
        LogTool.debug("开始升级----" + url + "---" + fileName);
        if (isRunning) {
            return;
        }
        isRunning = true;
        initRemoteView();

        try {
            boolean isSuccess = downloadUpdateFile(url, fileName);
            if (mProgressListener != null) {
                mProgressListener.onSuccess(isSuccess);
            }
            if (isSuccess) {
                Uri uri = Uri.fromFile(new File(fileName));
                Intent installIntent = new Intent(Intent.ACTION_VIEW);
                installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                installIntent.setDataAndType(uri, "application/vnd.android.package-archive");
                startActivity(installIntent);
                try {
                    updateNotificationManager.cancel(0);
                } catch (Exception ex) {
                    LogTool.error(ex.getMessage(), ex);
                }
            } else {
                Notification notification = new Notification.Builder(AppUpdateService.this)
                        .setContentTitle(getString(R.string.app_name))
                        .setContentText("下载失败")
                        .setSmallIcon(R.drawable.download)
                        .build();
                updateNotificationManager.notify(0, notification);
            }


        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 初始化状态栏进度条
     */
    private void initRemoteView() {
        try {
            updateNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
            //状态栏提醒内容
            updateNotification = new Notification.Builder(this)
                    .setTicker("版本更新下载")
                    .setWhen(System.currentTimeMillis())
                    .setSmallIcon(R.drawable.download).build();
            updatePendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, RemoteViews.class), 0);
            updateNotification.contentIntent = updatePendingIntent;
            //状态栏提醒内容
            updateNotification.contentView = new RemoteViews(getApplication().getPackageName(), R.layout.progress);
            updateNotification.contentView.setProgressBar(R.id.progressBar1, 100, 0, false);
            updateNotification.contentView.setTextViewText(R.id.textView1, "0%");
            // 发出通知
            updateNotificationManager.notify(0, updateNotification);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 下载文件
     *
     * @param downloadUrl
     * @param filepath
     * @return
     * @throws Exception
     */
    private boolean downloadUpdateFile(String downloadUrl, String filepath) {
        try {
            int downloadCount = 0;
            int currentSize = 0;
            long totalSize = 0;
            int updateTotalSize = 0;
            boolean result = false;
            HttpURLConnection httpConnection = null;
            InputStream is = null;
            FileOutputStream fos = null;
            File temp = new File(filepath + ".tmp");
            if (temp.getParentFile().isDirectory()) {
                temp.getParentFile().mkdirs();
            }
            try {
                URL url = new URL(downloadUrl);
                httpConnection = (HttpURLConnection) url.openConnection();
                httpConnection.setRequestProperty("User-Agent", "PacificHttpClient");
                if (currentSize > 0) {
                    httpConnection.setRequestProperty("RANGE", "bytes=" + currentSize + "-");
                }
                httpConnection.setConnectTimeout(20000);
                httpConnection.setReadTimeout(120000);
                updateTotalSize = httpConnection.getContentLength();
                if (httpConnection.getResponseCode() == 404) {
                    throw new Exception("fail!");
                }
                is = httpConnection.getInputStream();
                fos = new FileOutputStream(temp, false);
                byte buffer[] = new byte[4096];
                int readsize = 0;
                while ((readsize = is.read(buffer)) > 0) {
                    fos.write(buffer, 0, readsize);
                    totalSize += readsize; // 为了防止频繁的通知导致应用吃紧,百分比增加10才通知一次
                    if ((downloadCount == 0) || (int) (totalSize * 100 / updateTotalSize) - 1 > downloadCount) {
                        downloadCount += 1;
                        try {
                            updateNotification.contentView.setProgressBar(R.id.progressBar1, 100, (int) totalSize * 100 / updateTotalSize,
                                    false);
                            updateNotification.contentView.setTextViewText(R.id.textView1, (int) totalSize * 100 / updateTotalSize + "%");
                            updateNotification.contentIntent = updatePendingIntent;
                            updateNotificationManager.notify(0, updateNotification);
                            if (mProgressListener != null) {
                                mProgressListener.onProgress((int) totalSize * 100 / updateTotalSize);
                                System.out.println("AppUpdateService.downloadUpdateFile"+(int) totalSize * 100 / updateTotalSize);
                            }
                        } catch (Exception ex) {
                            LogTool.error(ex.getMessage(), ex);
                        }
                    }
                }
                temp.renameTo(new File(filepath));
                temp.delete();
            } finally {
                if (httpConnection != null) {
                    httpConnection.disconnect();
                }
                if (is != null) {
                    is.close();
                }
                if (fos != null) {
                    fos.close();
                }
                result = updateTotalSize > 0 && updateTotalSize == totalSize;
                if (!result) { //下载失败或者为下载完成
                    new File(filepath).delete();
                }
            }
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }
}

贴出状态栏的布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="fill_parent"
              android:layout_height="fill_parent"
              android:gravity="center_vertical"
              android:orientation="horizontal"
    >

    <ImageView
        android:id="@+id/imageView1"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:paddingLeft="5dp"
        android:scaleType="centerInside"
        android:src="@drawable/download"
        />


    <ProgressBar
        android:id="@+id/progressBar1"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="0dp"
        android:layout_height="4dp"
        android:layout_marginLeft="10dp"
        android:layout_marginRight="5dp"
        android:layout_weight="1"
        android:progressDrawable="@drawable/progressbar_style"
        />


    <TextView
        android:id="@+id/textView1"
        android:layout_width="40dp"
        android:layout_height="wrap_content"
        android:layout_marginLeft="5dp"
        android:layout_marginRight="5dp"
        android:text="100%"
        android:textColor="#888888"
        android:textSize="14sp"/>

</LinearLayout>

这里贴代码比较多,没什么讲解,但是如果你对照着每行代码写下来,应该不需要我讲解就会明白了。

核心代码已经全部给出,如有疑问,可以“简书”找 “小追兵”留言。

注意

状态栏的图片setSmallIcon(R.drawable.download),这里的图片不要太大(我的6464 px),还有注意放在drawable文件夹下,否则可能会异报常。改了,如果还报异常,clean项目应该就好了。*

文件从网络下载,写流的部分,不是我写,搬砖搬来的。

最后:有需要Shadowsock翻墙账号可以私聊。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,398评论 25 707
  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,350评论 0 17
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,016评论 4 62
  • 第一次有想脱单的心情, 不是因为孤单, 不是因为遇见了喜欢的他, 是因为,害怕,怕自己这样下去,失去了喜欢一个人的...
    风琴玉爅阅读 191评论 0 1
  • 曾经小时候的我梦想着有一天挣好多好多的钱,买一个大房子,让我得家人都住一起。可是长大后,就清楚的知道这只是一个梦...
    安静前进阅读 525评论 0 0