Android高效率编码-第三方SDK详解系列(三)——JPush推送牵扯出来的江湖恩怨,XMPP实现推送,自定义客户端推送


很久没有更新第三方SDK这个系列了,所以更新一下这几天工作中使用到的推送,写这个系列真的很要命,你要去把他们的API文档大致的翻阅一遍,而且各种功能都实现一遍,解决各种bug各种坑,不得不说,极光推送真坑,大家使用还是要慎重,我们看一下极光推送的官网

  • https://www.jpush.cn/common/

推送比较使用,很多软件有需要,所以在这个点拿出来多讲讲,我们本节课奇会讲到

  • AndroidPn推送
  • JPush推送
  • JPush实现聊天

一.推送的原理

既然要使用推送,首先我们还是要来了解一下原理,因为我们不仅要使用JPush来实现推送,我们还得自己来实现一个推送,有因必有果,哦弥陀佛!!

推送的方式有两种,大家使用Git的时候也耳熟能详

  • push

    服务端有消息的时候主动向客户端推送消息

  • pull

    客户端向服务器拉取消息,没有消息的话不做操作

但是我们一般也不会去用pull方式,看JPush这个名字就知道,用的是push,因为pull去拉的话,就像ViewGroup要遍历一遍所有的View一样,麻烦,所有又费流又费电量,push推送是服务端要发什么消息就直接发过来了,所有要方便些许

而推送的方式也有很多种,我这里列举几种

  • C2DM云端推送

    Google官方的一个轻量级服务,依赖官方的C2DM服务器,优势在于可以直接和服务器进行通讯,但是我们基本上是无视他的,为什么?我大天朝

  • MQTT协议

    这个我们也可以忽略,因为这个可以说技术积累不够吧,很不稳定,虽然很小,但是要是一个推送都不稳定,那就成了鸡肋的东西了

  • RSMB实现推送

    这个协议有想了解的可以看下这位前辈的Blog:http://blog.csdn.net/jjmm2009/article/details/19496849
    我们这里用一张图来简单说明下

    这个协议我们不做太多的概括

  • XMPP协议

    XMPP协议可谓鼎鼎大名,基本XML的协议,是一个开源的协议,采用的通信模式是C/S(客户端/服务端),而且操作起来相对来说比较简单,同样的,因为基于XML实现,所以数据传输格式也是XML,分布式的特点,这个协议我们就药来多唠叨几句了

    他的工作原理

    客户端连接服务端——服务端进行认证——客户端请求操作——交互

我们等下讲会说一个AndroidPn的推送就是基于XMPP协议的

  • 第三方(JPush,BaiDu之类)

    现在各大平台都有自己的推动服务了,百度,小米,极光,等等等等….


二.AndroidPn实现推送

AndroidPn大家可能用的还是相对要少一点吧

  • 地址: https://sourceforge.net/projects/androidpn/

我们简单的来聊聊这个框架

  • 他是基于XMPP协议
  • 包含完整的服务端和客户端
  • 基于openfire开源工程

那我们来实现以下,其实实现起来还是稍微有点复杂的,我们要进行以下的几个步骤

  • 安装TomCat本地服务器(模拟服务器)
  • 下载AndroidPn源码部署在TomCat上
  • 实现推送后台
  • 集成AndroidPn客户端代码
  • 通过浏览器完成推送

我们慢工出细活,一步步来实现

1.安装TomCat服务器

这里我们就不重复讲了,有需要的看这篇十分简单的实现TomCat服务器的搭建

  • Android服务器——TomCat服务器的搭建

我们开启服务器

我们只要在浏览器能看到

就说明配置完成了

2.下载AndroidPn源码部署在TomCat上

官网地址上面说了

  • 地址: https://sourceforge.net/projects/androidpn/

我们直接进去可以看到

我们直接点击Download就可以下载了,他下面有一段描述

An open source project to provide push notification support for Android -- a xmpp based notification server and a client tool kit.

意思就是说:一个开源项目提供推送式通知支持Android——基于xmpp的通知服务器和客户端工具;

我们下载好之后就直接解压

我们可以直接进入bin文件里面启动服务器

到这里,我们的服务器是启动好了,我们可以直接在浏览器里面输入

  • http://127.0.0.1:7070/

哈哈,到这里,我们基本的环境就已经部署了,这里我说明一下,AndroidPn的代码下载之后解压随便解压在什么地方,以为之前已经启动了TomCat的原因,所以他会直接依赖

3.实现AndroidPn Client代码

客户端的实现,其实还是分了若干个步骤的,我们一点点来书写,首先我们需要下载一个asmack.jar,用于XMPP传输协议的实现

  • asmack官网:https://github.com/Flowdalic/asmack
  • asmack下载地址:http://asmack.freakempire.de/

这里这么多,我就随便下载了一个最新的

好了,我们直接新建一个项目——AndroidPn

然后开始部署了,首先肯定是把jar包放在libs目录下,接着,我们添加网络权限

<!--网络权限-->
<uses-permission android:name="android.permission.INTERNET"/>

最后需要配置我们的秘钥了,在res目录下新建一个raw文件夹,在文件夹下新建一个android.properties文件,添加

apiKey=1234567890
xmppHost=192.168.1.100(你的本地IP)
xmppPort=5222

这些都做完了的话,我们可以直接去清单文件里面注册服务,我们的服务从哪里来?看这里

这是前辈写好的类,我们可以直接拿来用,我会提供这个Demo给大家下载的,好了,我们可以去注册了

 <!--推送服务--><service
            android:name=".client.NotificationService"android:enabled="true"android:label="NotificationService"><intent-filter><action android:name="com.lgl.androidpn.client.NotificationService" /></intent-filter></service>

我们回到MainActivity里面

package com.lgl.androidpn;import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;import com.lgl.androidpn.client.ServiceManager;public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ServiceManager serviceManager = new ServiceManager(this);//设置通知栏图标serviceManager.setNotificationIcon(R.mipmap.ic_launcher);//启动服务serviceManager.startService();}
}

只要我们启动程序,这个服务就启动了,在此之前,我们还得家点权限

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

现在我们可以运行一下程序,毫无疑问,我们现在的程序是空的,但是在后台可以看到活动

这个时候我们就可以去实现推送了

然后我们接收到了

这里也说明一下,这个AndroidPn小案例也只是告诉大家这个推送的实现原理,以及我们亲手去实现了一遍推送之后得到的宝贵经验,并不推荐使用这个,API过时了,有点蛋疼,而且2010年就已经停止更新了,不推荐使用,我们只要了解原理就可以了

  • AndroidPn Demo:http://download.csdn.net/detail/qq_26787115/9526807

三.JPush实现推送

终于到我们的主角了Jpush极光推送,说实话这个也有点坑

  • 官方地址: https://www.jpush.cn/common/

我们根据官网的API文档来讲解

1.集成JPush推送环境

JPush我就不再多做什么介绍了,我简单说一下他的有点(来自网络)

  • SDK采用自定义的协议保持长连接,电量,流量都相对要少
  • 服务器的架构比较先进
  • 高并发可扩展性的云服务

最后加一句,还是有点坑,哈哈,不过正常使用绝对是没有问题的

第三方SDK现在基本上已经不用一步步来了,我们直接新建一个工程——LGLJPush

我们在后台新建一个应用

我们可以根据他的文档来

  • http://docs.jpush.io/guideline/android_guide/#sdk

首先下载SDK

  • http://docs.jpush.io/resources/

然后添加权限

 <!-- Required 一些系统要求的权限,如访问网络等--><uses-permission android:name="${applicationId}.permission.JPUSH_MESSAGE" /><uses-permission android:name="android.permission.RECEIVE_USER_PRESENT" /><uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.WAKE_LOCK" /><uses-permission android:name="android.permission.READ_PHONE_STATE" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.WRITE_SETTINGS" /><uses-permission android:name="android.permission.VIBRATE" /><uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /><uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <!-- Optional for location --><uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /><uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /><uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /><uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" /><uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />

再增加一个自定义的权限

<permissionandroid:name="${applicationId}.permission.JPUSH_MESSAGE"android:protectionLevel="signature" />

接着我们在application中进行各种注册了

 <!-- Required SDK核心功能--><activity
             android:name="cn.jpush.android.ui.PushActivity"android:configChanges="orientation|keyboardHidden" android:theme="@android:style/Theme.NoTitleBar" android:exported="false"><intent-filter><action android:name="cn.jpush.android.ui.PushActivity" /><category android:name="android.intent.category.DEFAULT" /><category android:name="${applicationId}" /></intent-filter></activity><!-- Required SDK核心功能--><service
             android:name="cn.jpush.android.service.DownloadService"android:enabled="true"android:exported="false" ></service><!-- Required SDK 核心功能--><!-- option since 2.0.5 可配置PushService,DaemonService,PushReceiver,AlarmReceiver的android:process参数 将JPush相关组件设置为一个独立进程 --><!-- 如:android:process=":remote" --><service
             android:name="cn.jpush.android.service.PushService"android:enabled="true"android:exported="false"><intent-filter><action android:name="cn.jpush.android.intent.REGISTER" /><action android:name="cn.jpush.android.intent.REPORT" /><action android:name="cn.jpush.android.intent.PushService" /><action android:name="cn.jpush.android.intent.PUSH_TIME" /></intent-filter></service><!-- Required SDK 核心功能 since 1.8.0 --><service
             android:name="cn.jpush.android.service.DaemonService"android:enabled="true"android:exported="true"><intent-filter ><action android:name="cn.jpush.android.intent.DaemonService" /><category android:name="${applicationId}"/></intent-filter></service><!-- Required SDK核心功能--><receiver
             android:name="cn.jpush.android.service.PushReceiver"android:enabled="true"android:exported="false"><intent-filter android:priority="1000"><action android:name="cn.jpush.android.intent.NOTIFICATION_RECEIVED_PROXY" /> <!--Required 显示通知栏 --><category android:name="${applicationId}" /></intent-filter><intent-filter><action android:name="android.intent.action.USER_PRESENT" /><action android:name="android.net.conn.CONNECTIVITY_CHANGE" /></intent-filter><!-- Optional --><intent-filter><action android:name="android.intent.action.PACKAGE_ADDED" /><action android:name="android.intent.action.PACKAGE_REMOVED" /><data android:scheme="package" /></intent-filter></receiver><!-- Required SDK核心功能--><receiver android:name="cn.jpush.android.service.AlarmReceiver" /><!-- User defined. 用户自定义的广播接收器--><receiver
             android:name="您自己定义的Receiver"android:enabled="true"><intent-filter><action android:name="cn.jpush.android.intent.REGISTRATION" /> <!--Required 用户注册SDK的intent--><action android:name="cn.jpush.android.intent.MESSAGE_RECEIVED" /> <!--Required 用户接收SDK消息的intent--><action android:name="cn.jpush.android.intent.NOTIFICATION_RECEIVED" /> <!--Required 用户接收SDK通知栏信息的intent--><action android:name="cn.jpush.android.intent.NOTIFICATION_OPENED" /> <!--Required 用户打开自定义通知栏的intent--><action android:name="cn.jpush.android.intent.ACTION_RICHPUSH_CALLBACK" /> <!--Optional 用户接受Rich Push Javascript 回调函数的intent--><action android:name="cn.jpush.android.intent.CONNECTION" /><!-- 接收网络变化 连接/断开 since 1.6.3 --><category android:name="${applicationId}" /></intent-filter></receiver><!-- Required . Enable it you can get statistics data with channel --><meta-data android:name="JPUSH_CHANNEL" android:value="developer-default"/><meta-data android:name="JPUSH_APPKEY" android:value="您应用applicationId对应的appKey" /> <!-- </>值来自开发者平台取得的AppKey-->

AS还是比较方便的,这里我们只要修改一下我们自定义的广播和开发者的key就可以,我们新建一个广播——JPushReceiver配置上去就可以了

现在我们可以来初始化了,JPush的初始化需要在Application中进行,所以我们还要新建一个JPushApplication

package com.lgl.lgljpush;import android.app.Application;import cn.jpush.android.api.JPushInterface;/*** 初始化JPush* Created by LGL on 2016/5/21.*/public class JPushApplication extends Application{@Overridepublic void onCreate() {super.onCreate();JPushInterface.setDebugMode(true);JPushInterface.init(this);}
}

接着定义一下广播

package com.lgl.lgljpush;import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;import cn.jpush.android.api.JPushInterface;/*** Jpsuh推送广播* Created by LGL on 2016/5/21.*/
public class JPushReceiver extends BroadcastReceiver {public static final String TAG = "JPushReceiver";@Overridepublic void onReceive(Context context, Intent intent) {Bundle bundle = intent.getExtras();String action = intent.getAction();if (JPushInterface.ACTION_REGISTRATION_ID.equals(intent.getAction())) {String regId = bundle.getString(JPushInterface.EXTRA_REGISTRATION_ID);Log.d(TAG, "[MyReceiver] 接收Registration Id : " + regId);} else if (JPushInterface.ACTION_MESSAGE_RECEIVED.equals(intent.getAction())) {Log.d(TAG, "[MyReceiver] 接收到推送下来的自定义消息: " + bundle.getString(JPushInterface.EXTRA_MESSAGE));} else if (JPushInterface.ACTION_NOTIFICATION_RECEIVED.equals(intent.getAction())) {Log.d(TAG, "[MyReceiver] 接收到推送下来的通知");int notifactionId = bundle.getInt(JPushInterface.EXTRA_NOTIFICATION_ID);Log.d(TAG, "[MyReceiver] 接收到推送下来的通知的ID: " + notifactionId);} else if (JPushInterface.ACTION_NOTIFICATION_OPENED.equals(intent.getAction())) {Log.d(TAG, "[MyReceiver] 用户点击打开了通知");//打开自定义的Activity} else if (JPushInterface.ACTION_RICHPUSH_CALLBACK.equals(intent.getAction())) {Log.d(TAG, "[MyReceiver] 用户收到到RICH PUSH CALLBACK: " + bundle.getString(JPushInterface.EXTRA_EXTRA));//在这里根据 JPushInterface.EXTRA_EXTRA 的内容处理代码,比如打开新的Activity, 打开一个网页等..} else if (JPushInterface.ACTION_CONNECTION_CHANGE.equals(intent.getAction())) {boolean connected = intent.getBooleanExtra(JPushInterface.EXTRA_CONNECTION_CHANGE, false);Log.w(TAG, "[MyReceiver]" + intent.getAction() + " connected state change to " + connected);} else {Log.d(TAG, "[MyReceiver] Unhandled intent - " + intent.getAction());}}
}

这里其实只要定义几个就行,我照着官方的Demo就把所有的广播都写了一遍了。回到我们的控制台

我们点击推送

OK,推送成功了,不过这里要注意的是,我曾一度的被这个问题困扰

可能也怪自己太轻视了吧,于是翻阅了一下资料,根据他的错误找到了问题的所在,其实我们的lib类库是需要加载在Gradle下的,所在,我们可以在build.gradle中的android根节点下配置

  sourceSets.main{jniLibs.srcDir 'libs'}

从而绑定我们的类库,我们看图

OK,我们的极光推送就集成成功了

2.自定义推送

JPush给我们提供了一个比较高级的用法,就是可以在客户端进行推送,需要我们做一些处理,具体是什么呢,我们可以参照一下JPush官方提供的服务端文档

  • http://docs.jpush.io/server/rest_api_v3_push/

文档中提到,我们需要一个Authorization

通过appKey:masterSecret的组合Base64算法得到的一个数字,这个masterSecret在我们的控制台

在下面的文章中,可能涉及到很多JPush的术语,有些可能我没有阐述到位的地方还是劳烦你多看看上面的文档哦!

推送的必填是平台和设备,JPush 当前支持 Android, iOS, Windows Phone 三个平台的推送。其关键字分别为:”android”, “ios”, “winphone”。
我们举个例子

  • 推送到所有平台:
{ "platform" : "all" }
  • 指定特定推送平台:
{ "platform" : ["android", "ios"] }
  • audience

推送设备对象,表示一条推送可以被推送到哪些设备列表。确认推送设备对象,JPush 提供了多种方式,比如:别名、标签、注册ID、分群、广播等。

  • all

如果要发广播(全部设备),则直接填写 “all”,我们一般也是填all

OK,我们开始第一步,用Base64算法得到我们的秘钥,网上搜索一下Base64的在线解码

  • http://www1.tc711.com/tool/BASE64.htm

开始解码

我们就可以得到秘钥了

  • YzNlNDIyODdiZjQ4Y2NmYWQ4N2MwNDEyOmFkMmNiNDMxZmNmZDAwMWIwNzBjNTlhMSA=

这里我们使用的是HttpClient的Post请求,所以你必须在Gradle的文件里面加入

 useLibrary 'org.apache.http.legacy'

看图更加形象一点

我们写个布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"android:orientation="vertical"android:padding="15dp"><EditText
        android:id="@+id/et_content"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="请输入消息" /><Button
        android:id="@+id/btn_send"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="5dp"android:text="发送" /></LinearLayout>

很简单,就一个输入框和一个按钮,我们要做的事情也很简单,点击发送按钮之后就发送消息,通过开启一个子线程去实现,大概的代码

package com.lgl.lgljpush;import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONException;
import org.json.JSONObject;import java.io.IOException;
import java.io.UnsupportedEncodingException;public class MainActivity extends AppCompatActivity {//发送按钮private Button btn_send;//文本框private EditText et_content;//要发送的文本private String text;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);btn_send = (Button) findViewById(R.id.btn_send);et_content = (EditText) findViewById(R.id.et_content);btn_send.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {//拿到输入框的内容text = et_content.getText().toString();//判断不能为nullif (!TextUtils.isEmpty(text)) {new Thread(runnable).start();} else {Toast.makeText(MainActivity.this, "请输入文字", Toast.LENGTH_SHORT).show();}}});}Runnable runnable = new Runnable() {@Overridepublic void run() {try {//Post初始化String url = "https://api.jpush.cn/v3/push";HttpClient httpClient = new DefaultHttpClient();HttpPost httpPost = new HttpPost(url);//添加头部信息httpPost.addHeader("Authorization", "Basic YzNlNDIyODdiZjQ4Y2NmYWQ4N2MwNDEyOjY4Zjg2MTIxMDM2YjNhODRmOWRhN2VhOA==");//定义Json请求httpPost.addHeader("Content-Type", "application/json");//添加字段JSONObject obj = new JSONObject();obj.put("platform", "all");obj.put("audience", "all");JSONObject notification = new JSONObject();JSONObject android = new JSONObject();android.put("alert", text);notification.put("android", android);obj.put("notification", notification);httpPost.setEntity(new StringEntity(obj.toString()));HttpResponse response = httpClient.execute(httpPost);//打印返回码Log.e("返回码", response.getStatusLine().getStatusCode() + "");} catch (JSONException e) {e.printStackTrace();Log.e("返回码", "JSONException");} catch (UnsupportedEncodingException e) {e.printStackTrace();Log.e("返回码", "UnsupportedEncodingException");} catch (ClientProtocolException e) {e.printStackTrace();Log.e("返回码", "ClientProtocolException");} catch (IOException e) {e.printStackTrace();Log.e("返回码", "IOException");} catch (StackOverflowError e) {Log.e("返回码", "StackOverflowError");}}};/*** 发送消息 Post方式发送** @param date* @return*/private String sendMessage(String date) {return "";}}

我自问我的代码写的还是逻辑很清晰的,相信你一定能看懂,好了,看下运行的返回码

那就说明成功了,我们看一下运行的结果

这里我用的是模拟器,所以不能输入中文,我们可以看到我发送的消息是ddd,然后手机就接收到了,其实,我们可以根据设备进行推送,而且可以指定推送内容,这样,是不是我们可以根据逻辑实现一个聊天的小应用?这里本来想写的。篇幅太多了,留着以后有时间写吧,感兴趣的可以自己去实现以下,根据注册ID去发送消息,JPush也给我们提供了接口

 Log.e("ID", JPushInterface.getRegistrationID(this));
  • Nexus 5x: 140fe1da9eab772de2c

OK,我们这篇就先到这里,感谢你的观看,觉得不错的可以点个赞

我的群:555974449

Demo下载:http://download.csdn.net/detail/qq_26787115/9527044

Android高效率编码-第三方SDK详解系列(三)——JPush推送牵扯出来的江湖恩怨,XMPP实现推送,自定义客户端推送...相关推荐

  1. Android高效率编码-第三方SDK详解系列(三)——JPush推送牵扯出来的江湖恩怨,XMPP实现推送,自定义客户端推送

    Android高效率编码-第三方SDK详解系列(三)--JPush推送牵扯出来的江湖恩怨,XMPP实现推送,自定义客户端推送 很久没有更新第三方SDK这个系列了,所以更新一下这几天工作中使用到的推送, ...

  2. Android高效率编码-第三方SDK详解系列(一)——百度地图,绘制,覆盖物,导航,定位,细腻分解!...

    Android高效率编码-第三方SDK详解系列(一)--百度地图,绘制,覆盖物,导航,定位,细腻分解! 这是一个系列,但是我也不确定具体会更新多少期,最近很忙,主要还是效率的问题,所以一些有效的东西还 ...

  3. Android高效率编码-第三方SDK详解系列(二)——Bmob后端云开发,实现登录注册,更改资料,修改密码,邮箱验证,上传,下载,推送消息,缩略图加载等功能

    Android高效率编码-第三方SDK详解系列(二)--Bmob后端云开发,实现登录注册,更改资料,修改密码,邮箱验证,上传,下载,推送消息,缩略图加载等功能 我的本意是第二篇写Mob的shareSD ...

  4. Android高效率编码-第三方SDK详解系列(一)——百度地图,绘制,覆盖物,导航,定位,细腻分解!

    Android高效率编码-第三方SDK详解系列(一)--百度地图,绘制,覆盖物,导航,定位,细腻分解! 这是一个系列,但是我也不确定具体会更新多少期,最近很忙,主要还是效率的问题,所以一些有效的东西还 ...

  5. android json格式解析,android之解析json数据格式详解

    1.JSON解析 (1).解析Object之一: 解析方法: 1 JSONObject demoJson =newJSONObject(jsonString); 2 String url = demo ...

  6. Android特效 五种Toast详解

    Toast是Android中用来显示显示信息的一种机制,和Dialog不一样的是,Toast是没有焦点的,而且Toast显示的时间有限,过一定的时间就会自动消失.而且Toast主要用于向用户显示提示消 ...

  7. Android面试基础之BroadcastReceiver详解(斗帝养成系列四)

    斗帝养成 大斗师,一至九星,斗气铠甲,斗气外放,聚气化固态,呈菱形.别说自己尽力了,那只是自欺欺人的假话. Android面试基础之Activity详解(斗帝养成系列一) Android面试基础之Se ...

  8. Android第三方登录详解2

    接着Android第三方登录详解1讲 1.找到友盟  文档中心 2.找到 3.将 UMSocialService mController = UMServiceFactory.getUMSocialS ...

  9. Android面试基础之ContentProvider详解(斗帝养成系列三)

    斗帝养成 斗师,一至九星,斗气纱衣,聚气化液态. 我匆忙了一生,我却留不下任何东西. Android面试基础之Activity详解(斗帝养成系列一) Android面试基础之Service详解(斗帝养 ...

  10. 移动端实时音视频详解(三):编码和封装

    移动端实时音视频详解(三):编码和封装 视频编码是本系列一个重要的部分,如果把整个流媒体比喻成一个物流系统,那么编解码就是其中配货和装货的过程,这个过程非常重要,它的速度和压缩比对物流系统的意义非常大 ...

最新文章

  1. iOS架构篇-4 架构模式MVVM
  2. Zotero文献管理 | Zotero下载使用、Zotero+坚果云实现多设备文献同步
  3. 51单片机C语言智能小车,基于51单片机智能小车的设计与实现
  4. 软考程序员java_软考程序员难吗?
  5. 二代测序 下载 NCBI sra 文件
  6. 服务器搭建说明以及接口调用文档
  7. 区块链入门教程(1)--概述
  8. Android 常用的语言代码与国家地区对照表
  9. linux中python编辑器_Python 编辑器
  10. 如何开启和关闭 win10卓越模式?
  11. python规模大小_Python语言的一个重要特点是它有较多的计算生态,简单理解为第三方体噢概念股的可用变成模块/函数库组建,这个规模有多大?...
  12. 工业控制信息安全资源汇总(国内篇)
  13. 浅谈快速沃尔什变换(FWT)快速莫比乌斯变换(FMT)
  14. 智能呼叫系统之客户互动中心
  15. 常用函数式接口及Stream流
  16. 艾美捷胆固醇肉豆蔻酸酯说明书和相关研究
  17. 什么是GRAY色彩空间
  18. 电力电子应用技术的matlab仿真
  19. 《关于大学生熬夜状况的调查》
  20. 一家企业怎样才算Cool?Gartner告诉你!

热门文章

  1. 2015/7/24 (等待回调,结果是盘中回调,盘末拉升,错过了进仓机会吗?详情进入...
  2. box2d之刚体,定制器笔记
  3. 七种qsort排序方法
  4. CamShift算法,OpenCV实现(3):CamShift算法
  5. 基于 Intel 的 Mac,如何使用机构恢复密钥的技巧
  6. 【Flutter】基础组件【03】Scaffold
  7. LintCode 寻找旋转排序数组中的最小值 II
  8. 【SQLAlchemy】SQLAlchemy技术文档(中文版)(上)
  9. Android 色彩设计理念
  10. PC-hosts 的使用 [可使电脑无法正常上网]