【android极光推送】—从客户端到后台,一文通吃

sion 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.WRITE_SETTINGS" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:name=".MyApplication"
        android:theme="@style/AppTheme">

        <!-- Required. For publish channel feature -->
        <!-- JPUSH_CHANNEL 是为了方便开发者统计APK分发渠道。-->
        <!-- 例如: -->
        <!-- 发到 Google Play 的APK可以设置为 google-play; -->
        <!-- 发到其他市场的 APK 可以设置为 xxx-market。 -->
        <!-- 目前这个渠道统计功能的报表还未开放。-->
        <meta-data android:name="JPUSH_CHANNEL" android:value="developer-default"/>
        <!-- Required. AppKey copied from Portal -->
        <meta-data android:name="JPUSH_APPKEY" android:value="52f7fd72d96df72e2a811d7c"/>

        <!-- Required -->
        <receiver
            android:name="cn.jpush.android.service.PushReceiver"
            android:enabled="true" >
            <intent-filter android:priority="1000">
                <action android:name="cn.jpush.android.intent.NOTIFICATION_RECEIVED_PROXY" />
                <category android:name="com.example.dell.imooc_jpushdemo"/>
            </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核心功能-->
        <activity
            android:name="cn.jpush.android.ui.PushActivity"
            android:configChanges="orientation|keyboardHidden"
            android:exported="false" >
            <intent-filter>
                <action android:name="cn.jpush.android.ui.PushActivity" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="com.example.dell.imooc_jpushdemo" />
            </intent-filter>
        </activity>
        <!-- Required SDK核心功能-->
        <service
            android:name="cn.jpush.android.service.DownloadService"
            android:enabled="true"
            android:exported="false" >
        </service>
        <!-- Required SDK核心功能-->
        <receiver android:name="cn.jpush.android.service.AlarmReceiver" />

        <!-- 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>

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

至此androidSDK的配置就完成了,现在我们可以去极光的去推送一下消息,如果应用可以收到推送,这说明配置正确。

wampServer服务端配置

服务端这里采用wampServer的方式,当然是处于简单方便的考虑,还可以使用其他的脚本语言来实现,而极光推送的官网也提供了相应的服务端SDK供大家使用

这里的继承方式采用官方提供的sdk(也就是本文介绍的第三种方法),当然,也可以使用PHP向推送方直接发送HTTP请求。

配置推送SDK

极光官方建议使用composer进行SDK安装,而且给了非常简单的介绍 
这里写图片描述

接下来介绍一下composer的安装。

通过composer配置

关于什么是composer,我个人把它理解为android里面的gradle,在android里面,我们可以在gradle中添加一句 “compile XXX”就能轻松引入第三方库,而composer的功能也是如此,composer是 PHP 用来管理依赖(dependency)关系的工具。你可以在自己的项目中声明所依赖的外部工具库(libraries),Composer 会帮你安装这些依赖的库文件。

这里简单介绍一下composer的下载,不放心的小伙伴可以去Composer中文网相关查看教程。

(1)从官网下载composer

传送门:Windows下载

一路next,安装完毕后在cmd里面输入composer

这里写图片描述

即说明下载成功。

(2)关于composer.phar

在查阅资料的时候,我看到了composer.phar文件,后来研究了一下,得出了一些结论。

composer.phar 可以理解为composer的一个命令集合,不需要无需下载配置composer环境,只需要在PHP环境下,即可被使用,基本的使用方法是

php composer.phar XXXX

XXX表示composer的命令

而我们在这第一步下载的composer.exe,它实际做的一件事情是下载composer各种命令,并把composer添加到环境变量中(这也是为什么我们安装完毕后,在dos界面输入composer能够有反应的原因),其用法是:

composer XXX

发现没,这里的composer无需使用php,就能够执行composer的命令

综上述所述,如果你不想安装composer.exe,下载一个composer.phar文件,然后在PHP环境下运行就可以了,二者没有太大的差别。

不过有一点需要注意的是,命令的运行目录,一定要是当前的项目目录(存放有composer.json)。

(3)编写composer.json文件

此文件的作用主要用来声明包之间的相互关系和其他的一些元素标签。 
接下来,我们来到项目目录(我的是wamp/www/push) 
在此目录下新建一个叫 composer.json的文件

这里写图片描述

在此文件里面输入后保存,退出;

{
    "require": {
        "jpush/jpush": "v3.5.*"
    }
}

(4)下载第三方库

接下来,我们需要在cmd当中不断的cd目录,一直进入到我们当前的push目录,这里教大家一个简单的方法。如果你装了git,那么直接右键push目录,点击git brash here 就能很方便地打开dos界面。

键入下载的命令:

composer install

或者

php composer.phar install

二者的区别请见第二点

这里写图片描述

在解释上面问题之前,需要讲讲composer.lock这个文件

在安装完所有需要的包之后,composer会生成一张标准的包版本的文件在composer.lock文件中。这将锁定所有包的版本。

使用composer.lock(当然是和composer.json一起)来控制你的项目的版本

这一点非常的重要,我们使用install命令来处理的时候,它首先会判断composer.lock文件是否存在,如果存在,将会下载相对应的版本(不会在于composer.json里面的配置),这意味着任何下载项目的人都将会得到一样的版本。

如果不存在composer.lock,composer将会通过composer.json来读取需要的包和相对的版本,然后创建composer.lock文件

这样子就可以在你的包有新的版本之后,你不会自动更新了,升级到新的版本,使用update命令即可,这样子就能获取最新版本的包并且也更新了你的composer.lock文件。

所以,第一次的话,使用install命令,之后的使用updata命令就好了。

全部下载完成后,我们的当前的项目目录应该是这个样子的。

这里写图片描述

编写推送接口

终于到了编写接口的部分了

为了方便的加载包文件,Composer自动生成了一个文件 vendor/autoload.PHP,我们可以方便只有的使用它在任何你需要使用的地方。

新建一个test.php文件 在里面实现我们的推送逻辑。

<?php
//composer下载下来的第三方SDK都放在vendor文件夹中
//注意路径
require 'vendor/jpush/jpush/autoload.php';

//接受post来的参数
$params=$_POST;

//创建应用的AppKey和master_secret,可以在极光应用后台查看
$app_key="52f7fd72d96df72e2a811d7c";
$master_secret="847d609885ec219f313b0c12";

/*
 * 官方代码
 * */
$client = new \JPush\Client($app_key, $master_secret);

$pusher = $client->push();
//设置平台
$pusher->setPlatform('all');

$pusher->addAllAudience();

//唯一标识符,暂不使用
//$pusher->addAlias($params['alias']);
// 简单地给所有平台推送相同的 alert 消息

$pusher->setNotificationAlert('Hello, JPush');
try {
    $result=$pusher->send();
    var_dump($result);
} catch (\JPush\Exceptions\JPushException $e) {
    // try something else here
    print $e;
}

官方文档里有更详细的介绍,相信大家结合上面的注释应该是可以看懂的。

这个API的作用是向所有的客户发送一条消息。

现在把wampServer运行起来,在浏览器输入这个接口。

http://localhost/push/test.php

这里写图片描述

如果调用成功,则说明接口正确~~

定向推送

到这里,从客户端到服务端的基本推送逻辑已经完成了,接下来我们做的一件事情就是实现定向的推送,即通过这个alias,将消息发送给指定的用户

android客户端改动

首先填写一个布局文件MainActivity.xml

效果如下: 
这里写图片描述

比较简陋,一个editText用于设定alias,一个editText用于输入目标alias,还有一个Spinner用于选择推送的类型。

xml代码:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
    tools:context="com.example.dell.imooc_jpushdemo.MainActivity">

    <LinearLayout
        android:orientation="vertical"
        android:gravity="center"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <EditText
            android:id="@+id/et_set_alias"
            android:gravity="center"
            android:hint="设定alias"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <Button
            android:id="@+id/bt_set"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="设置"
           />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="当前alias:未设置"
            android:id="@+id/tv_alias"
            android:textSize="18sp"
            android:layout_marginTop="22dp" />

        <EditText
            android:id="@+id/et_alias"
            android:gravity="center"
            android:layout_marginTop="20dp"
            android:hint="输入目标alias"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
        />

            <Spinner
                android:id="@+id/push_type"
                android:layout_width="match_parent"
                android:layout_height="30dp"
             />

            <Button
                android:id="@+id/bt_send"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="发送"
                android:layout_marginTop="23dp" />

            <TextView
                android:id="@+id/tv_result"
                android:text="推送结果"
                android:gravity="center"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="20dp"
        />
        </LinearLayout>
</RelativeLayout>

接下来,我们编写MainActivity当中的逻辑

public class MainActivity extends AppCompatActivity {
    //接口地址,根据本机的IP地址改动
    private final String url="http://192.168.0.128/push/test.php";
    //返回的结果
    private TextView tvResult;
    private Button btSend; //发送推送请求
    private Spinner mSpinner; //选择推送方式
    //推送种类
    private String pushType;
    //spinner的适配器
    private ArrayAdapter<String> adapter;
    //设置alias的按钮
    private Button btSetAlias;
    //显示用户设置的alias
    private TextView tvAlias;
    private String alias;

    //更新UI
    private Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            StringBuffer sb = (StringBuffer) msg.obj;
            tvResult.setText(sb.toString());
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mSpinner= (Spinner) findViewById(R.id.push_type);
        tvResult= (TextView) findViewById(R.id.tv_result);
        btSend= (Button) findViewById(R.id.bt_send);
        btSetAlias= (Button) findViewById(R.id.bt_set);
        tvAlias= (TextView) findViewById(R.id.tv_alias);
        //spinner内的文字
        String[] strings=getResources().getStringArray(R.array.push_type);
        List<String> list=new ArrayList<>();
        for(String s:strings){
            list.add(s);
        }

        adapter=new ArrayAdapter<String>(MainActivity.this,android.R.layout.simple_spinner_item,list);
        //第三步:为适配器设置下拉列表下拉时的菜单样式。
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        //第四步:将适配器添加到下拉列表上
        mSpinner.setAdapter(adapter);

        mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                pushType=adapter.getItem(i);
                    /* 将mySpinner 显示*/
                adapterView.setVisibility(View.VISIBLE);
            }

            @Override
            public void onNothingSelected(AdapterView<?> adapterView) {
                adapterView.setVisibility(View.VISIBLE);
            }
        });

        //设置alias
        btSetAlias.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                alias=((EditText)findViewById(R.id.et_set_alias)).getText().toString();
                //调用SDK接口
                JPushInterface.setAlias(getBaseContext(),alias, new TagAliasCallback() {
                    @Override
                    public void gotResult(int i, String s, Set<String> set) {
                        tvAlias.setText("当前alias:"+alias);
                        Toast.makeText(MainActivity.this, "设置成功", Toast.LENGTH_SHORT).show();
                    }
                });
            }
        });

        btSend.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                sendRequest();
            }
        });
    }

    private void sendRequest() {
        new Thread(new Runnable() {
            @Override
            public void run() {

                String alias=((EditText)findViewById(R.id.et_alias)).getText().toString();
                try {
                    // 传递的数据
                    String data ="&alias="+URLEncoder.encode(alias,"UTF-8")
                            +"&push_type="+URLEncoder.encode(pushType,"UTF-8");
                    URL httpUrl = new URL(url);
                    HttpURLConnection urlConnection = (HttpURLConnection) httpUrl.openConnection();
                    urlConnection.setRequestMethod("POST");

                    // 设置请求的超时时间
                    urlConnection.setReadTimeout(5000);
                    urlConnection.setConnectTimeout(5000);

                    //调用conn.setDoOutput()方法以显式开启请求体
                    urlConnection.setDoOutput(true); // 发送POST请求必须设置允许输出
                    urlConnection.setDoInput(true); // 发送POST请求必须设置允许输入

                    //setDoInput的默认值就是true
                    OutputStream ost = urlConnection.getOutputStream();

                    PrintWriter pw = new PrintWriter(ost);
                    pw.print(data);
                    pw.flush();
                    pw.close();

                    BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));
                    StringBuffer sb = new StringBuffer();
                    String s;
                    while ((s = bufferedReader.readLine()) != null) {
                        sb.append(s);
                        Log.d("111", "run: "+sb.toString());
                    }
                    Message msg = new Message();
                    msg.obj = sb;
                    handler.sendMessage(msg);
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

R.array.push_type里面的内容:(在res/value/arrays.xml中配置,如果没有这个xml文件,手动创建)

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string-array name="push_type">
        <item>push Type</item>
        <item>new_task</item>
        <item>send_task</item>
        <item>get_task</item>
        <item>finish_task</item>
    </string-array>
</resources>

注释都比较清楚,网络操作使用的是调用android原生的API,当然也可以使用一些网络框架来完成。

最后,我们对服务端的代码进行改动

服务端改动

服务端test.php

<?php
//composer下载下来的第三方SDK都放在vendor文件夹中
//注意路径
require 'vendor/jpush/jpush/autoload.php';

//接受post来的参数
$params=$_POST;

//创建应用的AppKey和master_secret,可以在极光应用后台查看
$app_key="52f7fd72d96df72e2a811d7c";
$master_secret="847d609885ec219f313b0c12";

$title='';
$alert='';
//接受客户端参数
@$alias=$_POST['alias'];
@$push_type=$_POST['push_type'];

if(empty($alias)){
   echo 'alias null';
   return;
}
switch ($push_type){
    case "new_task":
        $title="新订单提醒";
        $alert="您有新的订单";
        break;
    case "send_task":
        $title='订单指派提醒';
        $alert='您的订单已被指派';
        break;
    case "get_task":
        $title='订单受理提醒';
        $alert='您有订单已被受理';
        break;
    case "finish_task":
        $title='订单完成提醒';
        $alert='您的订单已完成';
        break;
}

/*
 * 官方代码
 * */
$client = new \JPush\Client($app_key, $master_secret);

$pusher = $client->push();
//设置平台
$pusher->setPlatform('all');

//唯一标识符
$pusher->addAlias($alias);

echo $alert." ".$title;

$pusher->setNotificationAlert($title, $alert);
try {
    $result=$pusher->send();
    var_dump($result);
} catch (\JPush\Exceptions\JPushException $e) {
    // try something else here
    print $e;
}

服务端这里新增了对post参数的接收,当然……这里的参数验证逻辑写的还不够严谨,在实际项目中肯定还要有更多的验证及错误信息反馈。

PHP端代码相对比较简单……当然,写的还不够优雅,不过已经可以实现基本的功能了~

至此,代码部分就全部完成了,现在让我们来测试一下效果!

效果测试

首先我开启了2台虚拟机,并且提前设置好了他们的alias

夜神模拟器: 
这里写图片描述

android模拟器: 
这里写图片描述

夜神模拟机的alias设为001,推送的type设为new_task 
as 的模拟机alias 设为002,推送的type设为send_task

接下来,我们点击推送

这里写图片描述 
可以看到,两台机器都成功收到了相应的推送!

结语(终于完成了……)

国庆期间花了不少时间折腾这个……现在才整理出来……(不过好在整理出来了)

这个demo从客户端到后台,相对比较全面地介绍了推送的相关内容,当然,这些内容在官网上都能找到相应的资料,而且这篇文章的代码还不够优雅(尤其是PHP部分),实现的功能还比较基础,所以要完成更加复杂功能的小伙伴,还需要去官网深入学习文档,以满足实际的需求。

好了~最后感谢坚持看到了这里的小伙伴(≧▽≦)/

posted on 2016-10-13 09:46  Sun‘刺眼的博客  阅读(1879)  评论(0编辑  收藏  举报

导航