Please contact: wechat: Michaelzhoujay for reprint

The original article can be accessedMy blog


If your product provides users with a Web service, like a Web page or AN Html5 page designed for mobile devices, then I’m guessing you’ll encourage users to share that content on other platforms, or via message mail.

Generally speaking, product managers will use various mechanisms to encourage users to actively complete sharing. Some products will reward users for completing sharing, such as points and coupons. The essence of sharing is spread based on user relationship, so that more people can access your product. If someone sees a shared link or page and gets a click, you need to do everything you can to convert them to your users. Improve the effect of clicking links, and increase the conversion rate of sharing products.

So this article is really about how to maximize share conversion on Android.

Infrastructure: URL routing

This is the basis for the next steps, without which much of the following can’t be done. URL routing means that all product pages in your App need to be URL reachable. There are a lot of great URL routing on Github, like ARouter from Alibaba’s technology team. With simple configuration and annotations, you can quickly build your own URL routing framework.

Let’s take a look at the basics.

For example, a news App provides three functional modules: news detail page, news topic page and news discussion page. Assuming the package name of the App we’re dealing with is com.zhoulujue.news, the connection of these modules should look something like this:

Pointing at the id = 123456 news details page: http://news.zhoulujue.com/article/123456/ point to id = 123457 news feature pages: http://news.zhoulujue.com/story/123457/ point to id = 123456 news discussion page: http://news.zhoulujue.com/article/123456/comments/Copy the code

Suppose the class names of these pages are:

News details page: ArticleActivity News Features page: StoryActivity News Discussion page: CommentsActivityCopy the code

So we need an administrative center that does two things:

  1. Distribute urls passed in from the outside world to each Activity to process.
  2. Manage the mapping between URL paths and activities.

To unify the entry, we create an entry Activity: RouterActivty, which declares to the system which links the App can open and accepts urls passed in from the outside world. First we declare it in the Manifest:

<activity
    android:name=".RouterActivty"
    android:theme="@android:style/Theme.Translucent.NoTitleBar">
    <intent-filter android:autoVerify="true">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data
            android:host="news.zhoulujue.com"
            android:pathPattern="/. *"
            android:scheme="http" />
        <data
            android:host="news.zhoulujue.com"
            android:pathPattern="/. *"
            android:scheme="https" />
    </intent-filter>
</activity>
Copy the code

The above declaration states that RouterActivty can open HTTPS/HTTP links for all domains news.zhoulujue.com. The RouterActivty in receive news.zhoulujue.com/article/123… You need to parse /article/123456/, find ArticleActivity, invoke it, and pass the ArticleActivity id 123456 as an argument.

Common Router frameworks manage correspondence by adding annotations to the Activity’s class name:

@Route(path = "/acticel/")
public class ArticleActivity extend Activity {... }Copy the code

It actually generates a Builder in Builder mode while processing the annotation and registers it with the admin saying it can handle the /acticel/ XXX subdomain.

The choice of Scheme is important: WAKE up the URL Scheme

We only declare Android :scheme=” HTTP “and Android :scheme=” HTTP”, but in fact many apps invoke apps in scheme-specific ways. For example, in the early days of iOS, when there was no UniversalLink, people invoked it like this.

Like taobao can use tbopen scheme, tbopen://item.taobao.com/item.htm?id=xxxx, for example, when you are in the web page after clicking on a link, the page will create a hidden iframe, use it to open a custom URL scheme, Browser response, send an Action to the system for android. The intent. The Action. The VIEW, the Data is tbopen://item.taobao.com/item.htm?id=xxxx intent, If the App has been modified according to the previous section, the system will raise the RouterActivity and pass the Intent.

So here’s the problem: how to select a URL Scheme so that the browser doesn’t respond, so your Scheme had better satisfy two conditions:

  1. Different from other applications: unique
  2. Different from scheme that browsers can already handle: particularity

In our hypothetical news App above, we can define Scheme as zljnews, so the URL sent in URL scheme will look like this:

Points to the id = 123456 news details page: zljnews://news.zhoulujue.com/article/123456/ point to id = 123457 news feature page: zljnews://news.zhoulujue.com/story/123457/ point to id = 123456 news discussion page: zljnews://news.zhoulujue.com/article/123456/comments/Copy the code

To avoid some applications preprocessing scheme and host, we also need to change the host of the URL scheme accordingly:

Pointing at the id = 123456 news details page: zljnews: / / zljnews/article / 123456 / point to id = 123457 news feature pages: zljnews: / / zljnews/story / 123457 / point to id = 123456 news discussion page: zljnews://zljnews/article/123456/comments/Copy the code

The declaration of the RouterActivity in our Manifest should be changed to:

<activity
    android:name=".RouterActivty"
    android:theme="@android:style/Theme.Translucent.NTitleBar">
    <intent-filter android:autoVerify="true">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data
            android:host="news.zhoulujue.com"
            android:pathPattern="/. *"
            android:scheme="http" />
        <data
            android:host="news.zhoulujue.com"
            android:pathPattern="/. *"
            android:scheme="https" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="zljnews" />
        <data android:host="zljnews" />
        <data android:pathPattern="/. *" />
    </intent-filter>
</activity>
Copy the code

App Links and Universal Links, from the official way

We assume that a use case: users in the impression wrote a note in the notes, notes, there is a link: http://news.zhoulujue.com/article/123456/. So the question is: what happens when the user clicks?

The answer is: most likely, the system will pop up a dialog box listing several apps and asking you which one you want to open.

This experience is not good enough, as the user path becomes longer and the conversion rate decreases. So we should remove this dialog box as much as possible. In fact, the previous chapter mentioned one method: Change the http://news.zhoulujue.com/article/123456/ to zljnews: / / zljnews/article / 123456 /, principle is that we chose look “uniqueness” of the scheme, But if the user doesn’t have your App installed, the experience is pretty bad and the user doesn’t react when they click on it.

That’s where AppLinks and UniversalLinks come in, which basically means that the domain name holder can prove to the system that he owns the domain name news.zhoulujue.com and that he owns the App. The system will directly invoke the App and pass the intent to the App.

How to configure AppLinks will not be described here, but refer to the official tutorial.

Another way to implement App Links

The AppLinks protocol, which Facebook announced at its F8 developer conference in 2014, was also a viable way to “link to jump apps” before Android’s AppLinks (Google I/O 15). I don’t want to go into details here, but you can use Facebook’s official introduction to implement it, which is very simple:

Facebook AppLinks

What about code that isn’t your own

There are a lot of ways to wake up your App in a web page, but these are all based on the premise that we can change the javascript code of the page, if the page is provided by a third party, for example, by an advertiser, the way is that the advertiser provides a landing page to put in your App, It may be difficult to push third parties to change their code as you want, but it’s much more cost-effective to just change the jump link to evoke the App. This is where Chrome’s recommended Intent scheme comes in:

<a href="intent://zljnews/recipe/100390954#Intent; scheme=zljnews; package=com.zhoulujue.news; end"> Intent scheme </a>
Copy the code

As the code shows, scheme fills in the same scheme we assumed above: ZLjnews, keeping the same. Package Enter the App package name: com.zhouluju. news. Refer to the official Intent specification of Chrome

What to do in wechat

As we all know, wechat is to limit the behavior of arousing App. There are all kinds of wechat arousing hack circulating in the street, but it is always unknown when it will be banned. Here is the official method of wechat:

As shown in the figure above, Zhihu uses micro-download to direct the flow of Zhihu’s App. This method is common to Android and iOS. Please refer to the official documents of Tencent wechat for the specific implementation method.

Optimization 1: Seamless experience from web to App

Suppose a scene, when users visit http://news.zhoulujue.com to read news, recommended to download the App, open the App at this time after the installation, the best experience, of course, is to help the user to open he wasn’t watching the news, just jump straight to the read the web version of the article. Best practice: when the user clicks download, write the URL of the current page to the ZIP header of the APK file. After the user installs the download, read the URL on startup, and then route to the news details page with the Router mentioned above. Now follow me step by step.

When downloading APK from a web page: write the path as the APK ZIP file header

Save the following Java code as writeapk.java and compile it using Javac.

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.zip.ZipFile;

/** * Created by michael on 16/9/8. */
public class WriteApk {

    public static void main(String[] args) {
        for (int i = 0; i < args.length; i++) {
            System.out.println(args[i]);
        }
        if (args.length < 2) {
            System.out.println("Wrong parameters! Usage : WriteApk path comment\n");
        }
        String path = args[0];
        String comment = args[1];
        writeApk(new File(path), comment);
        System.out.println("Complete! File lies in " + path);
        try {
            ZipFile zipFile = new ZipFile(new File(path));
            System.out.println("Zip file comment = " + zipFile.getComment());
        } catch(IOException e) {
            e.printStackTrace();
            System.out.println("Zip file comment read failed!"); }}public static void writeApk(File file, String comment) {
        ZipFile zipFile = null;
        ByteArrayOutputStream outputStream = null;
        RandomAccessFile accessFile = null;
        try {
            zipFile = new ZipFile(file);
            String zipComment = zipFile.getComment();
            if(zipComment ! =null) {
                return;
            }

            byte[] byteComment = comment.getBytes();
            outputStream = new ByteArrayOutputStream();

            outputStream.write(byteComment);
            outputStream.write(short2Stream((short) byteComment.length));

            byte[] data = outputStream.toByteArray();

            accessFile = new RandomAccessFile(file, "rw");
            accessFile.seek(file.length() - 2);
            accessFile.write(short2Stream((short) data.length));
            accessFile.write(data);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(zipFile ! =null) {
                    zipFile.close();
                }
                if(outputStream ! =null) {
                    outputStream.close();
                }
                if(accessFile ! =null) { accessFile.close(); }}catch (Exception e) {

            }

        }
    }

    /** * byte arrays are converted to short */
    private static byte[] short2Stream(short data) {
        ByteBuffer buffer = ByteBuffer.allocate(2);
        buffer.order(ByteOrder.LITTLE_ENDIAN);
        buffer.putShort(data);
        buffer.flip();
        returnbuffer.array(); }}Copy the code

Then write the URL to APK using the following command:

$java WriteAPK /path/to/your/APK http://news.zhoulujue.com/article/12345/
Copy the code

When first opened by user: Reads the URL and opens it

Read the URL you wrote in the ZIP file header when the App is first opened as follows:

public static String getUnfinishedURL(Context context) {
    // Get the cached APK file
    File file = new File(context.getPackageCodePath());
    byte[] bytes;
    RandomAccessFile accessFile = null;
    // Find the information written by writeapk.java from the specified location
    try {
        accessFile = new RandomAccessFile(file, "r");
        long index = accessFile.length();
        bytes = new byte[2];
        index = index - bytes.length;
        accessFile.seek(index);
        accessFile.readFully(bytes);
        int contentLength = stream2Short(bytes, 0);
        bytes = new byte[contentLength];
        index = index - bytes.length;
        accessFile.seek(index);
        accessFile.readFully(bytes);
        return new String(bytes, "utf-8");
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if(accessFile ! =null) {
            try {
                accessFile.close();
            } catch(IOException ignored) { ignored.printStackTrace(); }}}return null;    
}
Copy the code

It then simply hands the return value of getUnfinishedURL to the Router to process, leading the user to the unfinished news details page.

Optimization 2: Controlled export of allowable traffic

The above content is all about how to get users into the App as much as possible. On the other hand, in order to improve the conversion rate of users, we need to reduce the jump rate of users, that is to say, try to avoid users being taken away from our App.

In many cases, if we were running an UGC community, we wouldn’t be able to control what urls people fill in when they create content, but as an open platform we would want users to be able to leverage tools to do what they’re focused on.

But if there are people on your platform who are advertising unrestricted, or using your platform to run a competitor’s product, the damage to a growing product can be devastating.

Best practice: Maintain a whitelist on the server, and the allowed domain names in this whitelist will be allowed to wake up, otherwise blocked.

The best way to intercept this is in a WebView, because most of the jump code is in the landing page to which the URL points. So we need to define the WebView client for the WebView

public class ControlledWebViewClient extends WebViewClient {

    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        Context context =  view.getContext();
        try {
            String host = Uri.parse(url.getOriginalUrl()).getHost();
            if(! isHostInWhiteList(host)) {return false;
            }
            
            String scheme = Uri.parse(url).getScheme();
            if(! TextUtils.isEmpty(scheme) && ! scheme.equals("http") && !scheme.equals("https")) {
                Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
                intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                context.getApplicationContext().startActivity(intent);
                return true; }}catch (Throwable t) {
            t.printStackTrace();
        }

        return false;
    }

    private boolean isHostInWhiteList(String) {
        // Check whether the whitelist is in the whitelist. }}Copy the code

To get as close to the correct Host as possible, note that in line 7 above, url.getoriginalurl () is used


Ok, App inside the use of links to jump around the thing is basically done, I hope it is helpful to you. If you have any other suggestions, please scan the qr code below to contact me, or leave a comment below