欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android App实现应用内部自动更新的最基本方法示例

程序员文章站 2024-02-26 13:18:58
这只是初步的实现,并没有加入自动编译等功能。需要手动更改更新的xml文件和最新的apk。    共涉及到四个文件! 一、客户端 and...

这只是初步的实现,并没有加入自动编译等功能。需要手动更改更新的xml文件和最新的apk。   
共涉及到四个文件!
一、客户端
androidupdatetestactivity:程序首页
main.xml:首页布局
update:更新类
softupdate_progress:更新等待界面

updage

package majier.test; 
 
import java.io.file; 
import java.io.fileoutputstream; 
import java.io.ioexception; 
import java.io.inputstream; 
import java.net.httpurlconnection; 
import java.net.malformedurlexception; 
import java.net.url; 
import java.util.hashmap; 
 
import javax.xml.parsers.documentbuilder; 
import javax.xml.parsers.documentbuilderfactory; 
 
import org.w3c.dom.document; 
import org.w3c.dom.element; 
import org.w3c.dom.node; 
import org.w3c.dom.nodelist; 
 
import android.app.alertdialog; 
import android.app.dialog; 
import android.app.alertdialog.builder; 
import android.content.context; 
import android.content.dialoginterface; 
import android.content.intent; 
import android.content.dialoginterface.onclicklistener; 
import android.content.pm.packagemanager.namenotfoundexception; 
import android.net.uri; 
import android.os.environment; 
import android.os.handler; 
import android.os.message; 
import android.view.layoutinflater; 
import android.view.view; 
import android.widget.progressbar; 
import android.widget.toast; 
 
public class update { 
  private static final int download = 1; 
  private static final int download_finish = 2; 
  private static final int connect_failed = 0; 
  private static final int connect_success = 1; 
  hashmap<string, string> mhashmap; 
  private string msavepath; 
  private int progress; 
  private boolean cancelupdate = false; 
  private context mcontext; 
  private progressbar mprogress; 
  private dialog mdownloaddialog; 
  private string mxmlpath; // 服务器更新xml存放地址 
 
  public update(context context, string xmlpath, string savepath) { 
    this.mcontext = context; 
    this.mxmlpath = xmlpath; 
    this.msavepath = savepath; 
  } 
 
  private handler mhandler = new handler() { 
    public void handlemessage(message msg) { 
      switch (msg.what) { 
      case download: 
        mprogress.setprogress(progress); 
        break; 
      case download_finish: 
        installapk(); 
        break; 
      default: 
        break; 
      } 
    }; 
  }; 
 
  /** 
   * 检查更新 
   */ 
  public void checkupdate() { 
    new thread(new runnable() { 
      @override 
      public void run() { 
        try { 
          url url = new url(mxmlpath); 
          httpurlconnection conn = (httpurlconnection) url 
              .openconnection(); 
          conn.setconnecttimeout(5000); 
          inputstream instream = conn.getinputstream(); 
          mhashmap = parsexml(instream); 
          message msg = new message(); 
          msg.what = connect_success; 
          handler.sendmessage(msg); 
        } catch (exception e) { 
          message msg = new message(); 
          msg.what = connect_failed; 
          handler.sendmessage(msg); 
        } 
      } 
    }).run(); 
  } 
 
  /** 
   * 访问服务器更新xml 
   */ 
  handler handler = new handler() { 
    @override 
    public void handlemessage(message msg) { 
      super.handlemessage(msg); 
      switch (msg.what) { 
      case connect_failed: 
        toast.maketext(mcontext, "访问服务器失败!", toast.length_short).show(); 
        break; 
      case connect_success: 
        if (null != mhashmap) { 
          int servicecode = integer.valueof(mhashmap.get("version")); 
          if (servicecode > getversioncode(mcontext)) { 
            shownoticedialog(); 
          } 
        } 
        break; 
      } 
    } 
  }; 
 
  /** 
   * 获取程序版本号 
   */ 
  private int getversioncode(context context) { 
    int versioncode = 0; 
    try { 
      versioncode = context.getpackagemanager().getpackageinfo( 
          mcontext.getpackagename(), 0).versioncode; 
    } catch (namenotfoundexception e) { 
      e.printstacktrace(); 
    } 
    return versioncode; 
  } 
 
  /** 
   * 是否更新提示窗口 
   */ 
  private void shownoticedialog() { 
    alertdialog.builder builder = new builder(mcontext); 
    builder.settitle("软件更新"); 
    builder.setmessage("检测到新版本,是否更新?"); 
    builder.setpositivebutton("更新", 
        new onclicklistener() { 
          @override 
          public void onclick(dialoginterface dialog, int which) { 
            dialog.dismiss(); 
            showdownloaddialog(); 
          } 
        }); 
 
    builder.setnegativebutton("取消", 
        new onclicklistener() { 
          @override 
          public void onclick(dialoginterface dialog, int which) { 
            dialog.dismiss(); 
          } 
        }); 
    dialog noticedialog = builder.create(); 
    noticedialog.show(); 
  } 
 
  /** 
   * 下载等待窗口 
   */ 
  private void showdownloaddialog() { 
    alertdialog.builder builder = new builder(mcontext); 
    builder.settitle("正在更新"); 
    final layoutinflater inflater = layoutinflater.from(mcontext); 
    view v = inflater.inflate(r.layout.softupdate_progress, null); 
    mprogress = (progressbar) v.findviewbyid(r.id.update_progress); 
    builder.setview(v); 
    builder.setnegativebutton("取消下载", 
        new onclicklistener() { 
          @override 
          public void onclick(dialoginterface dialog, int which) { 
            dialog.dismiss(); 
            cancelupdate = true; 
          } 
        }); 
    mdownloaddialog = builder.create(); 
    mdownloaddialog.show(); 
    downloadapk(); 
  } 
 
  /** 
   * 涓嬭浇apk鏂囦欢 
   */ 
  private void downloadapk() { 
    new downloadapkthread().start(); 
  } 
 
  /** 
   * 下载程序 
   */ 
  private class downloadapkthread extends thread { 
    @override 
    public void run() { 
      try { 
        if (environment.getexternalstoragestate().equals( 
            environment.media_mounted)) { 
 
          url url = new url(mhashmap.get("url")); 
          httpurlconnection conn = (httpurlconnection) url 
              .openconnection(); 
          conn.connect(); 
          int length = conn.getcontentlength(); 
          inputstream is = conn.getinputstream(); 
 
          file file = new file(msavepath); 
          if (!file.exists()) { 
            file.mkdir(); 
          } 
          file apkfile = new file(msavepath, mhashmap.get("name")); 
          fileoutputstream fos = new fileoutputstream(apkfile); 
          int count = 0; 
          byte buf[] = new byte[1024]; 
          do { 
            int numread = is.read(buf); 
            count += numread; 
            progress = (int) (((float) count / length) * 100); 
            mhandler.sendemptymessage(download); 
            if (numread <= 0) { 
              mhandler.sendemptymessage(download_finish); 
              break; 
            } 
 
            fos.write(buf, 0, numread); 
          } while (!cancelupdate); 
          fos.close(); 
          is.close(); 
        } 
      } catch (malformedurlexception e) { 
        e.printstacktrace(); 
      } catch (ioexception e) { 
        e.printstacktrace(); 
      } 
 
      mdownloaddialog.dismiss(); 
    } 
  }; 
   
  /** 
   * 安装apk 
   */ 
  private void installapk() { 
    file apkfile = new file(msavepath, mhashmap.get("name")); 
    if (!apkfile.exists()) { 
      return; 
    } 
 
    intent i = new intent(intent.action_view); 
    i.setdataandtype(uri.parse("file://" + apkfile.tostring()), 
        "application/vnd.android.package-archive"); 
    mcontext.startactivity(i); 
  } 
 
  private hashmap<string, string> parsexml(inputstream instream) 
      throws exception { 
    hashmap<string, string> hashmap = new hashmap<string, string>(); 
    // 实例化一个文档构建器工厂 
    documentbuilderfactory factory = documentbuilderfactory.newinstance(); 
    // 通过文档构建器工厂获取一个文档构建器 
    documentbuilder builder = factory.newdocumentbuilder(); 
    // 通过文档通过文档构建器构建一个文档实例 
    document document = builder.parse(instream); 
    // 获取xml文件根节点 
    element root = document.getdocumentelement(); 
    // 获得所有子节点 
    nodelist childnodes = root.getchildnodes(); 
    for (int j = 0; j < childnodes.getlength(); j++) { 
      // 遍历子节点 
      node childnode = (node) childnodes.item(j); 
      if (childnode.getnodetype() == node.element_node) { 
        element childelement = (element) childnode; 
        // 版本号 
        if ("version".equals(childelement.getnodename())) { 
          hashmap.put("version", childelement.getfirstchild() 
              .getnodevalue()); 
        } 
        // 软件名称 
        else if (("name".equals(childelement.getnodename()))) { 
          hashmap.put("name", childelement.getfirstchild() 
              .getnodevalue()); 
        } 
        // 下载地址 
        else if (("url".equals(childelement.getnodename()))) { 
          hashmap.put("url", childelement.getfirstchild() 
              .getnodevalue()); 
        } 
      } 
    } 
    return hashmap; 
  } 
} 

androidupdatetestactivity

package majier.test; 
 
import android.app.activity; 
import android.os.bundle; 
import android.os.environment; 
 
public class androidupdatetestactivity extends activity { 
  /** called when the activity is first created. */ 
  @override 
  public void oncreate(bundle savedinstancestate) { 
    super.oncreate(savedinstancestate); 
    setcontentview(r.layout.main); 
    update(); 
  } 
   
  private void update() { 
    string sdpath = environment.getexternalstoragedirectory() + "/"; 
    string msavepath = sdpath + "boiler/"; 
    update updatemanager = new update(this, 
        "http://localhost:8011/abcd.xml", msavepath); 
    updatemanager.checkupdate(); 
  } 
} 

main.xml

<?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:orientation="vertical" > 
 
  <textview 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="@string/hello" /> 
 
</linearlayout> 

softupdate_progress.xml

<?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="wrap_content"> 
  <progressbar 
    android:id="@+id/update_progress" 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    style="?android:attr/progressbarstylehorizontal" /> 
</linearlayout> 

每次生成新的apk前,需要修改系统的版本号。

Android App实现应用内部自动更新的最基本方法示例

修改version code 和version name。上面的代码可以看出,系统是根据version code来判断是否需要更新的。version name作为一个版本名称。
这里我建议version code从10开始,这样方面名称修改(1.1、1.2)。
修改完成后,生成系统。然后将apk文件放在服务端的文件下。

二、服务端
服务端主要是建立一个网址供用户下载apk。在iis上新建网站

http://localhost:8011/。将更新文件和更新的xml放在目录下。 

Android App实现应用内部自动更新的最基本方法示例

version.xml格式

<update> 
  <version>12</version> 
  <name>boilerandroid_1.1</name> 
  <url>http://192.168.0.33:8011/boilerandroid.apk</url> 
</update> 

version对应着新程序的version code;
name随便起名;
url对应apk的下载路径。

在这里有可能会遇见一个问题,访问url路径时iis报错。主要是因为iis并不认识apk,不知道如何处理。
这里我们在iis中新增安卓程序的mime类型,来使apk支持下载。
在“iis管理器”中查看所建立的网站——mime类型——添加。
文件扩展名:.apk
mime类型:application/vnd.android.package-archive

Android App实现应用内部自动更新的最基本方法示例

这样就可以下载了。
目前只是一个简单的自动更新程序。我们可以看出,其中版本号需要自己填写,而且要与xml中的对应,apk需要生成后放在更新网址下。
这么的人为操作,很容易造成失误。因此,接下来我们要研究下自动发布更新版本,并且版本号与svn对应,在提交svn后,自动改变程序的版本号。