热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Android:应用宝省流量更新

应用宝省流量更新介绍应用宝省流量更新(SDK),是应用宝提供给开发者轻松实现应用省流量更新的功能,可以帮助开发者缩短更新过程,提高应用下载量。在每次应用升级更新时,只需更新部分数据而无需下载

应用宝省流量更新介绍

应用宝省流量更新(SDK),是应用宝提供给开发者轻松实现应用省流量更新的功能,可以帮助开发者缩短更新过程,提高应用下载量。在每次应用升级更新时,只需更新部分数据而无需下载完整大小的安装包,帮用户节省了流量,也大大提高了产品升级速度。


注册开发者

  • 进入腾讯开放平台

  • 点击移动应用,进入登录界面

    移动应用

    登录界面

  • 使用QQ号码登录或注册新账号,登录后进入注册开发者界面,可选择注册为个人或公司开发者

    注册界面


创建应用

  • 注册成功后,进入管理中心,点击创建应用

    管理中心

    创建应用

  • 填写应用信息,提交审核

    提交审核


实现省流量更新

  • 邮件申请渠道号

    申请方法

    申请示例

  • 添加引用TMAssistantSDK_selfUpdate_201407240950

  • 修改AndroidManifest.xml

    • 添加服务
    <service android:name="com.tencent.tmassistantsdk.downloadservice.TMAssistantDownloadSDKService"
    android:exported="false"
    android:process=":TMAssistantDownloadSDKService" >

    service>
    • 添加权限

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

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
  • 添加检查更新代码

    • SoftwareUpgrade.java

      软件升级管理对象

    import android.content.Context;
    import android.content.DialogInterface;
    import android.content.Intent;
    import android.os.Handler;
    import android.os.Looper;
    import android.view.View;

    import com.tencent.tmassistantsdk.common.TMAssistantDownloadSDKTaskState;
    import com.tencent.tmassistantsdk.selfUpdateSDK.ITMSelfUpdateSDKListener;
    import com.tencent.tmassistantsdk.selfUpdateSDK.TMSelfUpdateSDK;
    import com.tencent.tmassistantsdk.selfUpdateSDK.TMSelfUpdateSDKUpdateInfo;

    import java.text.DecimalFormat;


    /**
    * 软件升级,利用应用宝的接口进行软件版本检查、软件升级
    */

    public class SoftwareUpgrade {

    private Context ctx = null;

    private static final String TAG = "SoftwareUpgrade";

    /**
    * AppID,应用上线后获得
    */

    private static final long APP_ID = 0000000000;

    /**
    * 渠道ID,邮件申请省流量更新后获得
    */

    private static final String CHANNEL_ID = "0000000";

    /**
    * 省流量更新SDK
    */

    private TMSelfUpdateSDK sdk = null;
    /**
    * 线程操作对象
    */

    private Handler handler = null;

    /**
    * 是否需要提醒更新(自动检查更新时需要)
    */

    private boolean isNeedNotify = false;

    /**
    * 软件更新的信息
    */

    private SoftwareUpdateInfo updateInfo = null;
    public static SoftwareUpdateInfo getUpdateInfo() {
    if (instance != null) {
    return instance.updateInfo;
    }
    return null;
    }

    /**
    * 更新提示对话框
    */

    private UpgradeDialog updateDialog = null;

    /**
    * 单例模式
    */

    private static SoftwareUpgrade instance = null;

    public static SoftwareUpgrade getInstance() {
    if (instance == null) {
    instance = new SoftwareUpgrade();
    }
    if (instance.sdk == null) { // 初始化失败,无法使用自动更新功能
    instance = null;
    }
    return instance;
    }

    /**
    * 初始化检查更新(软件启动时调用)
    */

    private SoftwareUpgrade() {

    // 得到主线程的Looper实例
    Looper looper = Looper.getMainLooper();
    handler = new Handler(looper);

    try {
    sdk = TMSelfUpdateSDK.getInstance();
    sdk.initTMSelfUpdateSDK(App.getInstance().getApplicationContext()
    , APP_ID, CHANNEL_ID, selfUpdateSDKListener);
    } catch (Exception ex) {
    sdk = null;
    LogUtil.d(TAG, "Init TMSelfUpdateSDK failed!");
    }
    }

    /**
    * 释放(退出软件时调用)
    */

    public static void release() {
    if (instance != null && instance.sdk != null) {
    instance.sdk.destroySelfUpdateSDK(instance.selfUpdateSDKListener);
    instance = null;
    }
    }

    /**
    * 检查软件是否需要更新(静默检查,不弹出提示对话框)
    */

    public static void checkNeedUpdate() {
    if (instance != null && instance.sdk != null) {
    instance.isNeedNotify = false;
    instance.sdk.checkNeedUpdate();
    }
    }

    /**
    * 检查软件是否需要更新(若有更新,则提醒用户更新)
    */

    public static void checkNeedUpdateNeedNotify(Context context) {
    if (instance != null && instance.sdk != null) {
    instance.ctx = context;
    instance.isNeedNotify = true;
    instance.sdk.checkNeedUpdate();
    }
    }

    /**
    * 开始省流量更新
    * 1、弹出更新信息对话框,询问用户是否更新
    * 2、若未安装应用宝,询问用户是否安装
    * 3、跳转到应用宝更新软件
    * @param ctx
    */

    public static void startSaveUpdate(final Context ctx) {
    if (instance != null && instance.sdk != null) {
    instance.updateDialog = new UpgradeDialog(ctx);
    instance.updateDialog.setVersion(instance.updateInfo.newVersion);
    instance.updateDialog.setUpdateContent(instance.updateInfo.updateContent);
    instance.updateDialog.setUpdateSize(instance.updateInfo.updateSize);

    instance.updateDialog.setUpdateButtonOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    try {
    // 检查应用宝是否安装
    int yybInstalled = instance.sdk.checkYYBInstalled();
    if (yybInstalled == TMAssistantDownloadSDKTaskState.UN_INSTALLED) {
    // 未安装应用宝,提示用户需要安装应用宝
    DialogUtil.showWarnDialog(ctx,
    R.string.msg_no_yyb, new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
    instance.sdk.startSaveUpdate(ctx);
    }
    });
    } else {
    // 打开应用宝更新
    instance.sdk.startSaveUpdate(ctx);
    instance.updateDialog.dismiss();
    }
    } catch (Exception e) {
    LogUtil.e(e);
    }
    }
    });
    }
    }

    /**
    * 省流量更新调用
    * 应用宝已经下载安装完毕,回到调用方的页面,调用方可以选择是否调用该方法,call起应用宝下载管理页去更新
    */

    public static void onResume() {
    if (instance != null && instance.sdk != null) {
    instance.sdk.onResume(App.getInstance().getApplicationContext());
    }
    }

    private ITMSelfUpdateSDKListener selfUpdateSDKListener = new ITMSelfUpdateSDKListener() {

    /**
    * 使用sdk自更新前,调用方法checkNeedUpdate检查是否需要更新时回调
    * 如果有更新包,则返回新包大小、增量包大小
    * @param tmSelfUpdateSDKUpdateInfo 这个参数,当继续往下走,下载 完成时,要传回来给接口
    */

    @Override
    public void OnCheckNeedUpdateInfo(final TMSelfUpdateSDKUpdateInfo tmSelfUpdateSDKUpdateInfo) {

    updateInfo = new SoftwareUpdateInfo();
    if (tmSelfUpdateSDKUpdateInfo != null
    && tmSelfUpdateSDKUpdateInfo.getStatus() == TMSelfUpdateSDKUpdateInfo.STATUS_OK
    && tmSelfUpdateSDKUpdateInfo.getNewApkSize() > 0) {

    // 解析版本号
    String url = tmSelfUpdateSDKUpdateInfo.getUpdateDownloadUrl();
    String version = "";
    if (url != null && url.length() > 0) {
    String[] temp = url.split("_");
    if (temp.length > 2) {
    version = temp[1];
    }
    }

    updateInfo.hasNewVersion = true;
    updateInfo.newVersion = version;
    updateInfo.updateCOntent= tmSelfUpdateSDKUpdateInfo.getNewFeature();
    updateInfo.updateSize = getDataSize(tmSelfUpdateSDKUpdateInfo.getNewApkSize());
    }

    if (isNeedNotify) { // 如果需要更新,则跳转到关于界面
    if (updateInfo.hasNewVersion) {
    DialogUtil.showWarnDialog(ctx,
    R.string.msg_has_new_version, new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
    // 跳转到关于界面进行更新
    MainActivity mainActivity = (MainActivity) ctx;
    if (mainActivity != null) {
    mainActivity.startActivity(new Intent(mainActivity, AboutActivity.class));
    }
    }
    });
    } else {
    App.getInstance().toast(R.string.msg_current_lastest_version_software);
    }
    }
    }

    /**
    * 省流量更新时回调
    * 检查应用宝状态,若未安装,则开始自动安装
    * @param url 指定任务的url
    * @param state 下载状态
    * @param errorCode 错误码
    * @param errorMsg 错误描述
    */

    @Override
    public void OnDownloadYYBStateChanged(String url, final int state,int errorCode, String errorMsg) {
    handler.post(new Runnable() {
    @Override
    public void run() {
    switch (state) {
    case TMAssistantDownloadSDKTaskState.DownloadSDKTaskState_WAITING:
    if (updateDialog != null) {
    updateDialog.startProgress();
    updateDialog.setProgressText(R.string.msg_waiting_install_yyb);
    }
    break;
    case TMAssistantDownloadSDKTaskState.DownloadSDKTaskState_DOWNLOADING:
    // 下方提供进度更新显示,此处不需要
    // if (updateDialog != null) {
    // updateDialog.setProgressText("正在下载应用宝");
    // }
    break;
    case TMAssistantDownloadSDKTaskState.DownloadSDKTaskState_PAUSED:
    // if (updateDialog != null) {
    // updateDialog.setProgressText("应用宝下载暂停");
    // }
    break;
    case TMAssistantDownloadSDKTaskState.DownloadSDKTaskState_SUCCEED:
    if (updateDialog != null) {
    updateDialog.setProgressText(R.string.msg_download_yyb_success);
    updateDialog.dismiss();
    }
    break;
    case TMAssistantDownloadSDKTaskState.DownloadSDKTaskState_FAILED:
    if (updateDialog != null) {
    updateDialog.dismiss();
    App.getInstance().toast(R.string.msg_download_yyb_failed);
    }
    break;
    default:
    // App.getInstance().toast(state);
    break;
    }
    }
    });
    }

    /**
    * 省流量更新时回调
    * 下载应用宝的进度
    * @param url 指定任务的url
    * @param receiveDataLen 已经接收的数据长度
    * @param totalDataLen 全部需要接收的数据长度(如果无法获取目标文件的总长度,此参数返回-1)
    */

    @Override
    public void OnDownloadYYBProgressChanged(final String url,final long receiveDataLen, final long totalDataLen) {
    handler.post(new Runnable() {
    @Override
    public void run() {
    if (updateDialog != null) {
    updateDialog.setProgress((int) (receiveDataLen * 100 / (double) totalDataLen));
    updateDialog.setProgressText(
    getDataSize(receiveDataLen) + " / " + getDataSize(totalDataLen));
    }
    }
    });
    }
    };

    /**
    * 计算数据大小,将bit转换为k或M的单位输出
    * @param dataSize
    * @return
    */

    private String getDataSize(long dataSize) {
    long kb = dataSize / 1024; // 单位k
    if (kb <1024) {
    return kb + "k";
    }
    double mb = dataSize / 1024.0 / 1024.0 + 0.05; // 转换为M,保留一位小数,四舍五入
    return new DecimalFormat(".0").format(mb) + "M";
    }

    /**
    * 软件更新信息
    */

    public class SoftwareUpdateInfo {
    /**
    * 是否有新版本
    */

    public boolean hasNewVersion = false;
    /**
    * 新版本号
    */

    public String newVersion = "";
    /**
    * 更新内容
    */

    public String updateCOntent= "";
    /**
    * 更新大小
    */

    public String updateSize = "";
    }
    }
    • UpgradeDialog.java

      软件升级信息对话框

    import android.app.AlertDialog;
    import android.content.Context;
    import android.support.annotation.StringRes;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.widget.Button;
    import android.widget.LinearLayout;
    import android.widget.ProgressBar;
    import android.widget.TextView;

    public class UpgradeDialog {

    private Context ctx = null;
    private AlertDialog dialog = null;
    private TextView tvVersion = null;
    private TextView tvUpdateCOntent= null;
    private LinearLayout layoutUpdateButton = null;
    private Button btnNotUpdate = null;
    private Button btnUpdate = null;
    private LinearLayout layoutUpdateProgress = null;
    private ProgressBar progressBar = null;
    private TextView tvUpdateProgress = null;

    public UpgradeDialog(Context context) {
    ctx = context;
    LayoutInflater layoutInflater = LayoutInflater.from(ctx);
    View view = layoutInflater.inflate(R.layout.dialog_notify_update, null);
    if (view != null) {
    tvVersion = (TextView) view.findViewById(R.id.tv_version);
    tvUpdateCOntent= (TextView) view.findViewById(R.id.tv_update_content);
    layoutUpdateButton = (LinearLayout) view.findViewById(R.id.layout_update_button);
    btnNotUpdate = (Button) view.findViewById(R.id.btn_not_update);
    btnUpdate = (Button) view.findViewById(R.id.btn_update);
    layoutUpdateProgress = (LinearLayout) view.findViewById(R.id.layout_update_progress);
    progressBar = (ProgressBar) view.findViewById(R.id.progressBar);
    tvUpdateProgress = (TextView) view.findViewById(R.id.tv_update_progress);

    AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
    builder.setTitle(R.string.layout_update_software_notify);
    builder.setView(view);
    dialog = builder.create();
    dialog.setCancelable(false);
    dialog.show();

    btnNotUpdate.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    dialog.dismiss();
    }
    });

    progressBar.setMax(100);
    }
    }

    public void setVersion(String version) {
    if (tvVersion != null) {
    tvVersion.setText(version);
    }
    }

    public void setUpdateContent(String updateContent) {
    if (tvUpdateContent != null) {
    tvUpdateContent.setText(updateContent);
    }
    }

    public void setUpdateSize(String newApkSize) {
    if (btnUpdate != null) {
    btnUpdate.setText(ctx.getString(R.string.layout_update_software_update_now, newApkSize));
    }
    }

    public void setUpdateButtonOnClickListener(View.OnClickListener listener) {
    if (btnUpdate != null) {
    btnUpdate.setOnClickListener(listener);
    }
    }

    public void dismiss() {
    if (dialog != null && dialog.isShowing()) {
    dialog.dismiss();
    }
    }

    public void setProgress(int value) {
    if (progressBar != null) {
    progressBar.setProgress(value);
    }
    }

    public void setProgressText(@StringRes int progress) {
    if (tvUpdateProgress != null) {
    tvUpdateProgress.setText(progress);
    }
    }

    public void setProgressText(String progress) {
    if (tvUpdateProgress != null) {
    tvUpdateProgress.setText(progress);
    }
    }

    public void startProgress() {
    if (layoutUpdateProgress != null) {
    layoutUpdateProgress.setVisibility(View.VISIBLE);
    }
    if (progressBar != null) {
    progressBar.setProgress(0);
    }
    if (layoutUpdateButton != null) {
    layoutUpdateButton.setVisibility(View.GONE);
    }
    }
    }
    • dialog_notify_update.xml

      软件升级提示界面

      软件升级提示


    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:padding="@dimen/app_content_border_size"
    android:layout_gravity="center">


    <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:gravity="center_horizontal">


    <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/layout_update_software_update_content"
    android:textColor="@color/app_title_textColor"
    android:textSize="@dimen/app_content_textSize"
    android:textStyle="bold"/>


    <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="@dimen/app_space_size"
    android:textColor="@color/app_title_textColor"
    android:textSize="@dimen/app_content_textSize"
    android:textStyle="bold"
    android:id="@+id/tv_version"/>

    LinearLayout>

    <ScrollView
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1"
    android:layout_marginTop="@dimen/app_content_border_size"
    android:minWidth="@dimen/software_upgrade_scrollbar_min_width"
    android:minHeight="@dimen/software_upgrade_scrollbar_min_height"
    android:scrollbars="vertical">

    <TextView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:textColor="@color/app_title_textColor"
    android:id="@+id/tv_update_content"/>

    ScrollView>

    <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:layout_marginTop="@dimen/app_content_border_size"
    android:visibility="gone"
    android:id="@+id/layout_update_progress">


    <ProgressBar
    style="?android:attr/progressBarStyleHorizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/progressBar" />

    <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:layout_marginTop="@dimen/app_space_size"
    android:textColor="@color/app_title_textColor"
    android:id="@+id/tv_update_progress"/>

    LinearLayout>

    <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:layout_marginTop="@dimen/app_content_border_size"
    android:id="@+id/layout_update_button">


    <Button
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_weight="1"
    android:text="@string/layout_update_software_not_update"
    android:id="@+id/btn_not_update"/>


    <Button
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_weight="1"
    android:text="@string/layout_update_software_update_now"
    android:id="@+id/btn_update"/>

    LinearLayout>

    LinearLayout>
    • MainActivity.xml

      主界面中初始化和释放软件升级管理对象

    public class MainActivity extends FragmentActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mainView = LayoutInflater.from(this).inflate(R.layout.activity_main, null);
    setContentView(mainView);

    // 初始化软件更新对象
    SoftwareUpgrade softwareUpgrade = SoftwareUpgrade.getInstance();
    if (softwareUpgrade == null) {
    app.setCanAutoUpdate(false); // 公共变量,用于标识自动更新对象是否初始化成功,若不成功,则屏蔽相关功能
    app.toast(R.string.msg_init_auto_update_software_failed);
    } else {
    app.setCanAutoUpdate(true);

    // 检查软件更新
    if (app.isAutoCheckSoftwareUpdate()) { // 是否开启自动检查更新功能
    SoftwareUpgrade.checkNeedUpdateNeedNotify(this); // 弹框提醒更新
    } else {
    SoftwareUpgrade.checkNeedUpdate(); // 不弹框提醒
    }
    }
    }

    @Override
    protected void onDestroy() {
    // 释放软件更新对象
    SoftwareUpgrade.release();

    super.onDestroy();
    }
    }
    • SoftwareSetupFragment.java

      软件设置界面,包含“自动检查更新”开关配置,关于。

      软件设置

    public class SoftwareSetupFragment extends BaseFragment {

    @Bind(R.id.btn_auto_check_software_update)
    ButtonSettingLayout btnAutoCheckSoftwareUpdate;
    @Bind(R.id.btn_about)
    ButtonSettingLayout btnAbout;

    @Override
    protected int getContentLayoutId() {
    return R.layout.fragment_software_setup;
    }

    /**
    * 自动检查更新的配置key
    */

    public static final String PREF_AUTO_CHECK_SOFTWARE_UPDATE = "auto_check_software_update";

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View rootView = super.onCreateView(inflater, container, savedInstanceState);

    initialize(rootView);
    return rootView;
    }

    @Override
    protected void initialize(View rootView) {
    ButterKnife.bind(this, rootView);

    // 自动检查软件更新
    btnAutoCheckSoftwareUpdate.setVisibility(app.isCanAutoUpdate() ? View.VISIBLE : View.GONE);
    btnAutoCheckSoftwareUpdate.setChecked(app.isAutoCheckSoftwareUpdate());
    btnAutoCheckSoftwareUpdate.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    prefs.edit().putBoolean(PREF_AUTO_CHECK_SOFTWARE_UPDATE, isChecked).commit();
    app.setAutoCheckSoftwareUpdate(isChecked);
    }
    });

    // 关于
    if (SoftwareUpgrade.getUpdateInfo() != null && SoftwareUpgrade.getUpdateInfo().hasNewVersion) {
    btnAbout.setIconColor(Color.RED);
    }
    btnAbout.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    parent.startActivityFromFragment(SoftwareSetupFragment.this,
    new Intent(getActivity(), AboutActivity.class), 0);
    }
    });
    }

    @Override
    protected void release() {
    ButterKnife.unbind(this);
    }
    }
    • AboutActivity.java

      关于界面,提供软件升级功能

      关于

    public class AboutActivity extends BaseActivity {

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

    btnCheckUpdate.setVisibility(app.isCanAutoUpdate() ? View.VISIBLE : View.GONE);
    if (SoftwareUpgrade.getUpdateInfo() != null) {
    if (SoftwareUpgrade.getUpdateInfo().hasNewVersion) {
    btnCheckUpdate.setText(getString(
    R.string.layout_update_software_update_now, SoftwareUpgrade.getUpdateInfo().newVersion));
    btnCheckUpdate.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    SoftwareUpgrade.startSaveUpdate(AboutActivity.this);
    }
    });
    } else {
    btnCheckUpdate.setText(R.string.msg_current_lastest_version_software);
    }
    }
    }

    @Override
    protected void onResume() {
    super.onResume();

    SoftwareUpgrade.onResume();
    }
    }
    • strings.xml
    <string name="msg_init_auto_update_software_failed">初始化自动更新功能失败!string>
    <string name="msg_current_lastest_version_software">当前为最新版本string>
    <string name="layout_update_software_update_now">更新(%1$s)string>
    <string name="layout_update_software_notify">软件升级提醒string>
    <string name="layout_update_software_update_content">新版特性string>
    <string name="layout_update_software_not_update">暂不更新string>
    <string name="msg_no_yyb">未安装应用宝,将开始安装应用宝!string>
    <string name="msg_has_new_version">软件有新版本,是否更新?string>
    <string name="msg_waiting_install_yyb">等待安装应用宝string>
    <string name="msg_download_yyb_success">应用宝下载成功,开始安装应用宝string>
    <string name="msg_download_yyb_failed">下载应用宝失败,请检查网络连接string>

接入省流量更新(获取官方介绍)

  • 进入应用

    应用详情

  • 点击基础服务中的更多,可进入省流量更新

    基础服务

  • 省流量更新页面中,点击查看详情,进入省流量更新详情页

    查看详情

    省流量更新



推荐阅读
  • 带添加按钮的GridView,item的删除事件
    先上图片效果;gridView无数据时显示添加按钮,有数据时,第一格显示添加按钮,后面显示数据:布局文件:addr_manage.xml<?xmlve ... [详细]
  • Nginx使用(server参数配置)
    本文介绍了Nginx的使用,重点讲解了server参数配置,包括端口号、主机名、根目录等内容。同时,还介绍了Nginx的反向代理功能。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 本文介绍了RxJava在Android开发中的广泛应用以及其在事件总线(Event Bus)实现中的使用方法。RxJava是一种基于观察者模式的异步java库,可以提高开发效率、降低维护成本。通过RxJava,开发者可以实现事件的异步处理和链式操作。对于已经具备RxJava基础的开发者来说,本文将详细介绍如何利用RxJava实现事件总线,并提供了使用建议。 ... [详细]
  • 本文介绍了H5游戏性能优化和调试技巧,包括从问题表象出发进行优化、排除外部问题导致的卡顿、帧率设定、减少drawcall的方法、UI优化和图集渲染等八个理念。对于游戏程序员来说,解决游戏性能问题是一个关键的任务,本文提供了一些有用的参考价值。摘要长度为183字。 ... [详细]
  • Imdevelopinganappwhichneedstogetmusicfilebystreamingforplayinglive.我正在开发一个应用程序,需要通过流 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 本文介绍了腾讯最近开源的BERT推理模型TurboTransformers,该模型在推理速度上比PyTorch快1~4倍。TurboTransformers采用了分层设计的思想,通过简化问题和加速开发,实现了快速推理能力。同时,文章还探讨了PyTorch在中间层延迟和深度神经网络中存在的问题,并提出了合并计算的解决方案。 ... [详细]
  • 本文介绍了响应式页面的概念和实现方式,包括针对不同终端制作特定页面和制作一个页面适应不同终端的显示。分析了两种实现方式的优缺点,提出了选择方案的建议。同时,对于响应式页面的需求和背景进行了讨论,解释了为什么需要响应式页面。 ... [详细]
author-avatar
mobiledu2502869603
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有