實現安卓app自動更新功能執行個體方案

來源:互聯網
上載者:User

安卓應用實現自動更新比較簡單,這裡跟大家介紹下。

1. web介面

需要提供一個介面供用戶端查詢更新狀態,並且在需要更新時,告知用戶端新APK地址。

介面參數如下:

    package   包名,因為有時候會出現同一個應用換包名打包的情況
    version 版本號碼,即android資訊清單檔裡面的versionCode
    channel 渠道號
    os 作業系統,android/ios。ios 這裡僅作預留。

 

之所以傳入這些欄位,是要在與伺服器端的包匹配時,務必滿足:

    package, channel, os 相等,並且伺服器端的version 大於 用戶端傳入的version

代碼如下:

os = request.GET.get('os')
pkg_name = request.GET.get('package')
channel = request.GET.get('channel')
version = request.GET.get('version')

if not os or not pkg_name or not channel or not version:
    return jsonify(**ret_dict)
pkg = Package.objects.filter(
    os=os,
    package=pkg_name,
    channel=channel,
    status__gt=config.PACKAGE_STATUS_NOT_UPDATE
).order_by('-version').first()
if pkg and int(version) < pkg.version:
    ret_dict['pkg_status'] = str(pkg.status)
    ret_dict['pkg_url'] = config.WEB_HOST + pkg.file.url
    ret_dict['update_prompt'] = pkg.info
return jsonify(**ret_dict)

2. 資料庫設計

由於web端使用的是django,所以可以很方便的給出運營同學可以操作的後台介面,如下:



注意紅框內的元素,運營同學在上傳時,是不允許修改的,而是由程式自動解析APK檔案得到後填入的。

具體的解析方法,我們稍後給出。

而對應的models代碼如下:

class Package(models.Model):
    file = models.FileField(u'檔案', upload_to=config.PACKAGE_UPLOAD_PATH)
    package = models.CharField(u'包名', max_length=255, blank=True, default='')
    version = models.IntegerField(u"版本號碼", blank=True, default=0, null=True)
    channel = models.CharField(u"渠道", max_length=128, blank=True, default='')
    status = models.IntegerField(u'更新狀態', default=config.PACKAGE_STATUS_NOT_UPDATE,
        choices=config.PACKAGE_UPDATE_STATUS)
    info = models.TextField(u'通知資訊', blank=True, null=True)
    os = models.CharField(u'作業系統', max_length=64, default=config.PACKAGE_CLIENT_UNKNOW,
        choices=config.PACKAGE_CLIENT_OS, blank=True, null=True)

    def __unicode__(self):
        _,name = os.path.split(self.file.name)
        return name

    class Meta:
        unique_together = ('package', 'version', 'channel', 'os')

    def save(self, * args, ** kwargs):
        # 檔案上傳成功後,檔案名稱會加上PACKAGE_UPLOAD_PATH路徑
        path,_ = os.path.split(self.file.name)
        if not path:
            if self.file.name.endswith('.apk'):
                self.os = config.PACKAGE_CLIENT_ANDROID
                path = os.path.join('/tmp', uuid.uuid4().hex + self.file.name)
                # logger.error('path: %s', path)
                with open(path, 'wb+') as destination:
                    for chunk in self.file.chunks():
                        destination.write(chunk)
                info = parse_apk_info(path)
                os.remove(path)
                self.package = info.get('package', '')
                self.version = info.get('version', 0)
                self.channel = info.get('channel', '')
            elif self.file.name.endswith('ipa'):
                self.os = config.PACKAGE_CLIENT_IOS

        super(self.__class__, self).save(*args, ** kwargs)

    def display_filename(self):
        _,name = os.path.split(self.file.name)
        return name
    display_filename.short_description = u"檔案"

3. APK檔案解析

def parse_apk_info(apk_path, tmp_dir='/tmp'):
    """
    擷取包名、版本、渠道:
    {'version': '17', 'channel': 'CN_MAIN', 'package': ‘com.fff.xxx'}
    :param apk_path:
    :return:
    """
    from bs4 import BeautifulSoup
    import os
    import shutil
    import uuid

    abs_apk_path = os.path.abspath(apk_path)
    dst_dir = os.path.join(tmp_dir, uuid.uuid4().hex)
    jar_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'apktool.jar'))
    cmd = 'java -jar %s d %s %s' % (jar_path, abs_apk_path, dst_dir)

    if isinstance(cmd, unicode):
        cmd = cmd.encode('utf8')

    # 執行
    os.system(cmd)

    manifest_path = os.path.join(dst_dir, 'AndroidManifest.xml')

    result = dict()

    with open(manifest_path, 'r') as f:
        soup = BeautifulSoup(f.read())
        result.update(
            version=soup.manifest.attrs.get('android:versioncode'),
            package=soup.manifest.attrs.get('package'),
        )

        channel_soup = soup.find('meta-data', attrs={'android:name': 'UMENG_CHANNEL'})
        if channel_soup:
            result['channel'] = channel_soup.attrs['android:value']

    shutil.rmtree(dst_dir)

    return result

當然,正如大家所看到的,我們需要依賴於 apktool.jar 這個檔案,具體大家可以在網上下載。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.