首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Android进程保活全攻略(上)

Android进程保活全攻略(上)

作者头像
老马的编程之旅
发布于 2022-06-22 02:03:41
发布于 2022-06-22 02:03:41
1.4K00
代码可运行
举报
文章被收录于专栏:深入理解Android深入理解Android
运行总次数:0
代码可运行

对于每个公司的APP来说,当然都希望自己APP的进程尽量的不被杀死,于是乎,就有了一些列进程保活的方法出现,网上也有很多关于这类的文章,但网上很多资料往往只告诉了思路,并未将实现代码展示,本次我的博客将分为上下两篇,阐述关于进程保活的所有方法,以及实现的方式,若有错漏之处,大家可以在博客进行留言。

**

1.进程保活-背景知识

** (1)什么时候系统会去杀死进程? Android系统会在内存不足的时候去将进程杀死,俗称Low Memory Killer,它是 基于linux内核的 OOM Killer(Out-Of-Memory killer)机制,内存不足时,优先杀oom_adj值高的进程。

既然知道了oom_adj值,那大家肯定想知道,如何去查看应用的oom_adj值呢? 系统进程oom值小于0,应用进程大于0,可以发现,系统的就是叼 我们可以通过 adb命令,去查看相应进程的oom_adj值,命令如下:

查看命令:adb shell ps | grep 进程名 | awk ‘{print $2}’ | xargs -i adb shell cat /proc/{}/oom_adj

这里我总结了各种类型进程的oom_adj值

(2)进程被杀的场景有哪些? 进程被杀死的场景很多,例如被第三方应用杀死(360管家等),关机等等,不同的场景调用的系统接口也是不同,同时杀死的oom_adj值范围也是不同的,于是我将这些场景总结成了一个表格,方便大家了解:

2.常见的保活拉起方式

了解进程被杀死的相关场景后,相信大家对进程保活已经有了初步的认识,接下来我将给大家介绍一下,现在市面上存在的各种常见的保活拉起方式,这些保活方式如下:

**a) 将Service设置为前台服务 b) 在service的onstart方法里返回 STATR_STICK c) 添加Manifest文件属性值为android:persistent=“true” d) 覆写Service的onDestroy方法 e) 监听一堆系统静态广播 f) 监听第三方应用的静态广播 g) AlarmManager唤醒 h) 账户同步,定时唤醒 i) 1像素悬浮层 j) GCM或其它3方推送 k) 应用间互相拉起 l) 心跳唤醒 m)Native进程拉起 n) 双进程守护**

1) 将Service设置为前台服务 思路:启用前台服务,主要是startForeground() 保活程度:一般情况下不被杀,部分定制ROM会在应用切到后台即杀 ,会被 force stop 杀死 代码实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Notificationnotification = newNotification(R.drawable.queen2, "有消息来了"
        , System.currentTimeMillis());


notification.setLatestEventInfo(this, "双11,上天猫!",
        "一律5折", null);

//设置通知默认效果
notification.flags = Notification.FLAG_SHOW_LIGHTS;

startForeground(1, notification);

2) 在service的onstart方法里返回 STATR_STICK 思路:其实就是onStartCommand中返回STATR_STICK 保活程度:有次数和时间的限制 ,会被 force stop 杀死 代码实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    // TODO Auto-generated method stub
    return START_STICKY;
    //return super.onStartCommand(intent, flags, startId);

}

3) 添加Manifest文件属性值为android:persistent=“true” 代码实现(清单文件中配置): 保活程度:一般情况下不被杀,会被 force stop 杀死

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<application android:name="PhoneApp"
    android:persistent="true"
    android:label="@string/dialerIconLabel"
    android:icon="@drawable/ic_launcher_phone">

注意:该方法需要系统签名

4) 覆写Service的onDestroy方法 思路:在onDestroy中再次启动该服务 保活程度:很弱,只在两种情况下work:正在运行里杀服务、DDMS里stop进程 代码实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Override
public void onDestroy() {
    Intent intent = new Intent(this, KeeLiveService.class);
    startService(intent);
    super.onDestroy();
}

5) 监听一堆系统静态广播 思路:在发生特定系统事件时,系统会发出响应的广播,通过在 AndroidManifest 中“静态”注册对应的广播监听器,即可在发生响应事件时拉活。 可以监听的系统静态广播列表如下:

保活强度:我们可以发现,这个方法都是监听系统的一些广播,所以我们需要在我们的应用中注册静态广播,但是静态广播又会出现问题,那就是在4.0版本以上,没有启动过的应用或Force-Stop后收不到静态广播,也就是说4.0以后,如果我们应用从未启动过,或者被Force-Stop杀死过,是无法接收到静态广播的。

如果是两个应用相互拉起,那么在一个应用内可发送带FLAG_INCLUDE_STOPPED_PACKAGES的Intent,那即使另一个应用也是以上两种情况,也可以接收到系统的广播 应用1的代码实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//应用1,发送拉起服务的广播
Intent intent = new Intent();
intent.setAction("com.action.keepLive");
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
this.sendBroadcast(intent);

应用2的代码实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<receiver android:name="com.yzy.supercleanmaster.receiver.KeepLiveReceiver">
    <intent-filter>
        <action android:name="com.action.keepLive" />
    </intent-filter>
</receiver>
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class KeepLiveReceiver extends BroadcastReceiver{
    //应用2中,接受应用1发送的广播,进行服务的拉起
    @Override
    public void onReceive(Context context, Intent intent) {
        Intent i = new Intent(context, KeeLiveService.class);
        context.startService(i);
    }
}

6) 监听第三方应用的静态广播 思路:通过反编译第三方 Top 应用,如:手机QQ、微信、支付宝、UC浏览器等,以及友盟、信鸽、个推等 SDK,找出它们外发的广播,在应用中进行监听,这样当这些应用发出广播时,就会将我们的应用拉活。

保活强度: 该方案的局限性除与系统广播一样的因素外,主要受如下因素限制: 1) 反编译分析过的第三方应用的多少 2) 第三方应用的广播属于应用私有,当前版本中有效的广播,在后续版本随时就可能被移除或被改为不外发,这些因素都影响了拉活的效果。

7) AlarmManager唤醒 思路:通过AlarmManager设置一个定时器,定时的唤醒服务 **保活强度:**killBackgroundProcess下,大部分情况work, 不敌force-stop,闹钟会被清除。 代码实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void startKeepLiveService(Context context, int timeMillis,String action) {
    //获取AlarmManager系统服务
    AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    //包装Intent
    Intent intent = newIntent(context,KeepLiveServie.class);
    intent.setAction(action);
    PendingIntent pendingIntent = PendingIntent.getService(context,0,intent, PendingIntent.FLAG_UPDATE_CURRENT);
    //添加到AlarmManager
    alarmManager.setRepeating(AlarmManager.RTC_WAKEUP,System.currentTimeMillis(),timeMillis,pendingIntent);
}

8) 账户同步,定时唤醒 **思路:**android系统里有一个账户系统,系统定期唤醒账号更新服务,同步的事件间隔是有限制的,最短1分钟。 难点:需要手动设置账户,你如何骗你的用户给你手动设置账户完了之后不卸载你,必须联网 代码实现: ① 建立数据同步系统(ContentProvider) 通过一个ContentProvider用来作数据同步,由于并没有实际数据同步,所以此处就直接建立一个空的ContentProvider即可

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class XXAccountProvider extends ContentProvider {
    public static final String AUTHORITY = "包名.provider";
    public static final String CONTENT_URI_BASE = "content://" + AUTHORITY;
    public static final String TABLE_NAME = "data";
    public static final Uri CONTENT_URI = Uri.parse(CONTENT_URI_BASE + "/" + TABLE_NAME);

    @Override
    public boolean onCreate() {
        return true;
    }

    @Nullable
    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        return null;
    }

    @Nullable
    @Override
    public String getType(Uri uri) {
        return new String();
    }

    @Nullable
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        return null;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        return 0;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        return 0;
    }
}

然后再Manifest中声明

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<provider
    android:name="**.XXAccountProvider"
    android:authorities="@string/account_auth_provider"
    android:exported="false"
    android:syncable="true"/>

② 建立Sync系统 (SyncAdapter) 通过实现SyncAdapter这个系统服务后, 利用系统的定时器对程序数据ContentProvider进行更新,具体步骤为: - 创建Sync服务

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class XXSyncService extends Service {
    private static final Object sSyncAdapterLock = new Object();
    private static XXSyncAdapter sSyncAdapter = null;

    @Override
    public void onCreate() {
        synchronized (sSyncAdapterLock) {
            if (sSyncAdapter == null) {
                sSyncAdapter = new XXSyncAdapter(getApplicationContext(), true);
            }
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return sSyncAdapter.getSyncAdapterBinder();
    }

    static class XXSyncAdapter extends AbstractThreadedSyncAdapter {
        public XXSyncAdapter(Context context, boolean autoInitialize) {
            super(context, autoInitialize);
        }

        @Override
        public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
            getContext().getContentResolver().notifyChange(XXAccountProvider.CONTENT_URI, null, false);
        }
    }
}
  • 声明Sync服务
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<service
    android:name="**.XXSyncService"
    android:exported="true"
    android:process=":core">
    <intent-filter>
        <action
            android:name="android.content.SyncAdapter"/>
    </intent-filter>
    <meta-data
        android:name="android.content.SyncAdapter"
        android:resource="@xml/sync_adapter"/>
</service>

其中sync_adapter为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
    android:accountType="@string/account_auth_type"
    android:allowParallelSyncs="false"
    android:contentAuthority="@string/account_auth_provide"
    android:isAlwaysSyncable="true"
    android:supportsUploading="false"
    android:userVisible="true"/>

参数说明: android:contentAuthority 指定要同步的ContentProvider在其AndroidManifest.xml文件中有个android:authorities属性。 android:accountType 表示进行同步的账号的类型。 android:userVisible 设置是否在“设置”中显示 android:supportsUploading 设置是否必须notifyChange通知才能同步 android:allowParallelSyncs 是否支持多账号同时同步 android:isAlwaysSyncable 设置所有账号的isSyncable为1 android:syncAdapterSettingsAction 指定一个可以设置同步的activity的Action。

  • 账户调用Sync服务 首先配置好Account(第三步),然后再通过ContentProvider实现 手动更新
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public void triggerRefresh() {
    Bundle b = new Bundle();
    b.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
    b.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
    ContentResolver.requestSync(
            account,
            CONTENT_AUTHORITY,
            b);
}

添加账号

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Account account = AccountService.GetAccount();
AccountManager accountManager = (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE);
accountManager.addAccountExplicitly(...)

``
同步周期设置

ContentResolver.setIsSyncable(account, CONTENT_AUTHORITY, 1); ContentResolver.setSyncAutomatically(account, CONTENT_AUTHORITY, true); ContentResolver.addPeriodicSync(account, CONTENT_AUTHORITY, new Bundle(), SYNC_FREQUENCY);

“ ③ 建立账号系统 (Account Authenticator) 通过建立Account账号,并关联SyncAdapter服务实现同步 - 创建Account服务

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class XXAuthService extends Service {
    private XXAuthenticator mAuthenticator;

    @Override
    public void onCreate() {
        mAuthenticator = new XXAuthenticator(this);
    }

    private XXAuthenticator getAuthenticator() {
        if (mAuthenticator == null)
            mAuthenticator = new XXAuthenticator(this);
        return mAuthenticator;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return getAuthenticator().getIBinder();
    }

    class XXAuthenticator extends AbstractAccountAuthenticator {
        private final Context context;
        private AccountManager accountManager;

        public XXAuthenticator(Context context) {
            super(context);
            this.context = context;
            accountManager = AccountManager.get(context);
        }

        @Override
        public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options)
                throws NetworkErrorException {
            // 添加账号 示例代码
            final Bundle bundle = new Bundle();
            final Intent intent = new Intent(context, AuthActivity.class);
            intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
            bundle.putParcelable(AccountManager.KEY_INTENT, intent);
            return bundle;
        }

        @Override
        public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options)
                throws NetworkErrorException {
            // 认证 示例代码
            String authToken = accountManager.peekAuthToken(account, getString(R.string.account_token_type));
            //if not, might be expired, register again
            if (TextUtils.isEmpty(authToken)) {
                final String password = accountManager.getPassword(account);
                if (password != null) {
                    //get new token
                    authToken = account.name + password;
                }
            }
            //without password, need to sign again
            final Bundle bundle = new Bundle();
            if (!TextUtils.isEmpty(authToken)) {
                bundle.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
                bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
                bundle.putString(AccountManager.KEY_AUTHTOKEN, authToken);
                return bundle;
            }

            //no account data at all, need to do a sign
            final Intent intent = new Intent(context, AuthActivity.class);
            intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
            intent.putExtra(AuthActivity.ARG_ACCOUNT_NAME, account.name);
            bundle.putParcelable(AccountManager.KEY_INTENT, intent);
            return bundle;
        }

        @Override
        public String getAuthTokenLabel(String authTokenType) {
//            throw new UnsupportedOperationException();
            return null;
        }

        @Override
        public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
            return null;
        }

        @Override
        public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options)
                throws NetworkErrorException {
            return null;
        }

        @Override
        public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options)
                throws NetworkErrorException {
            return null;
        }

        @Override
        public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features)
                throws NetworkErrorException {
            return null;
        }
    }
  • 声明Account服务
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<service
    android:name="**.XXAuthService"
    android:exported="true"
    android:process=":core">
    <intent-filter>
        <action
            android:name="android.accounts.AccountAuthenticator"/>
    </intent-filter>
    <meta-data
        android:name="android.accounts.AccountAuthenticator"
        android:resource="@xml/authenticator"/>
</service>

其中authenticator为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?xml version="1.0" encoding="utf-8"?>
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
    android:accountType="@string/account_auth_type"
    android:icon="@drawable/icon"
    android:smallIcon="@drawable/icon"
    android:label="@string/app_name"
    />
  • 使用Account服务 同SyncAdapter,通过AccountManager使用   - 申请Token主要是通过 [AccountManager.getAuthToken]系列方法   - 添加账号则通过 [AccountManager.addAccount]   - 查看是否存在账号通过 [AccountManager.getAccountsByType]

保活强度: 该方案适用于所有的 Android 版本,包括被 forestop 掉的进程也可以进行拉活。最新 Android 版本(Android N)中系统好像对账户同步这里做了变动,该方法不再有效。

本篇介绍了进程保活的一些方法,由于篇幅原因,1像素悬浮层、应用间互相拉起、心跳唤醒等方法将在下一篇进行介绍。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2016-11-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
以文件修改时间戳做 CSS、JS 等文件的版本号,减少主动清理更新缓存的次数
每次修改 WordPress 的 css 和 js 文件,都要主动清理文件的缓存才能生效,尤其是采用了 oss 和 cdn 回源。使用函数 filemtime() 获取文件修改时间戳,并以此做版本号,
Yangsh888
2022/03/23
1K0
WordPress 通过模板文件和自带的函数引入 css/js 的两种方法
WordPress 引入css/js 是我们制作主题时首先面对的一个难点,任何一款主题都要加载自己的 css,js,甚至很有可能还需要加载 Jquery 文件,网上方法特多,说法不一,我们今天借鉴 wordpress 官方最新的 twentysixteen 主题来学习总结一下 WordPress 引入 css/js 各种常用方法,以及最优化的加载方法。
Yangsh888
2022/03/28
2.1K0
WordPress主题开发,从入门到精通。
相关文档:https://www.wpzhiku.com/document/wordpress-plugin-basics/
房东的狗丶
2023/02/17
11.2K0
WPJAM「静态文件」:一键合并 WordPress 插件和主题的 JS 和 CSS 文件,加快页面加载速度
每个插件和主题可能有自己的 CSS 和 JavaScript 内联代码或者文件,如果 CSS 和 JavaScript 内联代码或者文件一多,就开始出现了两个比较难受的问题:
Denis
2023/04/13
7.3K0
WPJAM「静态文件」:一键合并 WordPress 插件和主题的 JS 和 CSS 文件,加快页面加载速度
如何在 WordPress 主题中使用本地托管的 Google 字体
前面我们介绍 WordPress 官方要求主题作者切换到本地托管字体,今天简单说说如何实现在本地托管的 Google 字体。
Denis
2023/04/13
9000
【WordPress优化二】去掉不必要的东西
主题选好了还没有完,因为WordPress系统自身还拥有一些优化的空间,因此我们可以对WordPress进行一些适量的优化。
夏末浅笑
2019/01/10
1.5K0
WordPress面试题
将域名解析到另一台主机涉及修改域名服务器记录(DNS)和邮件服务器记录(MX)。以下是一般步骤:
777nx
2023/11/16
9440
HTML中css和js链接版本号的用途
在搜索引擎中搜索关键字 .htaccess 缓存,你可以搜索到很多关于设置网站文件缓存的教程,通过设置可以将css、js等不太经常更新的文件缓存在浏览器端,这样访客每次访问你的网站的时候,浏览器就可以从缓存中获取css、js等静态文件,而不必从你的服务器再次下载读取,这样在一定程度上加快了网站的打开速度,又可以节约一下你的服务器流量。
阳光岛主
2019/02/18
6K0
Google Fonts导致WordPress 速度问题的三个解决方案
本来实在是不想写这个的,因为相关方法在圈子里面已经烂大街了。但无奈,一些客户将近期的Google Fonts导致 WordPress 打开速度慢的现象归咎于我的主题——真TM 比窦娥还冤。所以,有必要在这里说下。 2015.2.26更新:Google Fonts 已可正常访问,相关插件可卸,本文已失效 Google Fonts导致WordPress 速度问题之原因 WordPress 自3.8+版本后加入了Google Fonts(别问我Google Fonts是什么了),然后捏,因为近期敏感周期(35号)
Jeff
2018/01/19
2.3K0
WordPress 路径相关函数总结(二):主题路径相关函数
上一篇文章是《WordPress 路径相关函数总结(一):站点路径相关函数》,今天这一篇则是针对WordPress 主题的函数,对于开发WordPress 主题的开发者很有帮助;相关函数也可以在WordPress 官方文档找到相应的更详细的用法。 还是以本站 http://devework.com 为例子: get_theme_root_uri() 获取存放主题的目录URI echo get_theme_root_uri(); //输出:http://devework.com/wp-content/them
Jeff
2018/01/19
1.3K0
WordPress 根据浏览器 user-agent 按需加载CSS 文件
在进行前端开发的时候,为了兼容性,比如hack 那个讨厌的IE 浏览器,我们常常需要<!--[if IE X]>这类IE 判断代码来实现hack 的效果。而在WordPress 中,我们可以借助PHP 的功能,通过判断浏览器user-agent 来按需加载CSS 文件——如此不仅仅只是对IE hack,对于chrome 、firefox 等主流浏览器也可以实现不同的样式效果。 先看下面的代码,添加到主题的functions.php 文件下: function dw_enqueue_styles() { gl
Jeff
2018/01/19
1.1K0
WordPress主题制作(四):制作头部模板header.php
当我们用文本编辑器打开从WordPress主题制作:开始前的准备下载的Yii-Candy中的 .php 文件,不难看出他们头部的代码都非常的相似!我们可以提取这部分相似的代码,放到一个单独的文件header.php中,各个页面想用这部分代码的时候再用WordPress的get_header()包含进去就可以达到所有页面头部内容一致,不再需要给每个页面都写一次这部分代码了,我们后期维护起来也至需要修改header.php即可。
Yiiven
2022/12/15
1.7K0
WordPress 函数:wp_enqueue_script() 安全引入 JS
​WordPress 主题最佳引用 js 文件的方法是使用 WordPress 内置的 wp_enqueue_script() 函数,通过该函数可以安全地将javascript 代码加入到 WordPress 创建的页面中,不仅可以最大限度地保证兼容性,还是提高 WordPress 性能效率的一个方法。
Yangsh888
2022/03/23
1.1K0
WordPress 5.4.2版本发布,BUG维护和安全更新
嗨!您位于 https://www.baidu.com 的站点已被成功升级到WordPress 5.4.2。
陌涛
2020/06/15
2.3K0
DW Replace Open Sans:将WordPress 后台中的open-sans字体加载源从Google Fonts换为360 CDN
针对最近因为Google fonts被墙导致WordPress 打开慢的问题,Jeff 在上一篇《Google Fonts导致WordPress 速度问题的三个解决方案》提出的方案中其中是禁止加载Google fonts ,但是禁止后WordPress 后台的英文字体会变成你当前系统默认的——不好看。为此Jeff 借助网络上的代码开发了这款插件,可一键将WordPress 后台中的open-sans字体加载源从Google Fonts替换为360的CDN 加载源。 2015.2.26更新:Google
Jeff
2018/01/19
9230
更改WordPress Gutenberg编辑器的宽度
要更改WordPress Gutenberg编辑器的宽度,您需要为其加载自定义CSS规则。以下是如何快速更改宽度的概述,包括加载样式表和添加将增加Gutenberg编辑器内容区域宽度的特定规则。
许都博客
2021/06/16
1K0
通过修改function文件来使WordPress网站加载速度更快
说明:WordPress由外国人开发的,使用了很多国外网站服务,比如Gravatar镜像、谷歌字体之类的,由于我们在国内,链接速度自然就慢了很多,有的还时不时的被墙,很影响使用,而且功能很强大,但是很多我们都不需要,这里我们可以通过修改function.php来精简WordPress,从而使网站速度变快。
用户8851537
2021/07/26
8170
禁止/移除 WordPress 4.2 中前台自动加载的 emjo 脚本
如果你更新了WordPress 最新版本的4.2 版本,查看网页源代码你会发现WordPress 会自动在head加载一段用于支持emjo 表情的脚本(JS+CSS),对于大部分人来说,这个是十分鸡肋的功能,反而影响加载速度。 原因分析 脚本就是类似下面的代码: <script type="text/javascript"> window._wpemojiSettings = {"baseUrl":"http:\/\/s.w.org\/images\/core\/emoji\/72x72\/","ext":
Jeff
2018/01/22
1.2K0
WordPress 开发之使用WordPress 3.8+后台图标(dashicons)
伴随着WordPress 3.8 的新界面,WordPress 官方为后台UI 引进了目前贼流行的webfont(又称 icon font),官方独立项目名为dashicons。webfont 本质上是图标——通过字体文件作为载体的矢量图,好处就是,可以比较方便自由修改大小、颜色等字体样式且不像图片那样放大会失真——所以说乔布斯炒起来的retina 屏幕不在话下了。那么,如果你是WordPress 开发者,你可以通过以下方式在你开发的主题或插件中使用WordPress 3.8+后台图标(dashicons)
Jeff
2018/01/19
1.4K0
WordPress 开发之使用WordPress 3.8+后台图标(dashicons)
WordPress 主题教程 #3:开始 Index.php
开始 Index.php 是从零开始创建 WordPress 主题系列教程的第三篇。在介绍了 WordPress 主题的一些规则和术语,以及对 WordPress 模板和模板文件了解之后,现在是开始动手创建 WordPress 主题了的时候。
Denis
2023/04/15
1.3K0
WordPress 主题教程 #3:开始 Index.php
推荐阅读
相关推荐
以文件修改时间戳做 CSS、JS 等文件的版本号,减少主动清理更新缓存的次数
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档