我试图得到我的朋友的名字和id与图形API v2.0,但数据返回空:

{
  "data": [
  ]
}

当我使用v1.0时,以下请求一切正常:

FBRequest* friendsRequest = [FBRequest requestForMyFriends];
[friendsRequest startWithCompletionHandler: ^(FBRequestConnection *connection,
                                              NSDictionary* result,
                                              NSError *error) {
    NSArray* friends = [result objectForKey:@"data"];
    NSLog(@"Found: %i friends", friends.count);
    for (NSDictionary<FBGraphUser>* friend in friends) {
        NSLog(@"I have a friend named %@ with id %@", friend.name, friend.id);
    }
}];

但是现在我找不到朋友了!


当前回答

虽然Simon Cross的回答是正确的,但我还是想用一个例子(Android)来补充一下。我会尽量概括一下,只关注这个问题。就我个人而言,我把东西存储在数据库中,所以加载很顺利,但这需要一个CursorAdapter和ContentProvider,这有点超出了这里的范围。

我自己来到这里,然后想,现在该怎么办!

这个问题

就像user3594351一样,我注意到朋友数据是空白的。我发现这是通过使用FriendPickerFragment。三个月前有用的,现在没用了。就连Facebook的例子也失败了。所以我的问题是“我如何手工创建FriendPickerFragment ?”

什么不起作用

Simon Cross的选项1还不足以邀请好友加入应用。Simon Cross还推荐了请求对话框,但它一次只允许5个请求。请求对话框还会在任何特定的Facebook登录会话中显示相同的朋友。不是有用的。

什么有效(总结)

第二种选择,需要付出一些努力。你必须确保你遵守Facebook的新规则:1)你是第二场比赛。)你有一个Canvas应用(Web Presence)你的应用已经在Facebook上注册了。这一切都是在Facebook开发者网站的设置下完成的。

为了在我的应用程序中模拟好友选择器,我做了以下工作:

创建显示两个片段的选项卡活动。每个片段显示一个列表。一个片段用于可用的朋友(/me/friends),另一个片段用于可邀请的朋友(/me/invitable_friends)。使用相同的片段代码来呈现两个选项卡。 创建一个AsyncTask从Facebook获取好友数据。一旦数据被加载,将其扔给适配器,适配器将把值呈现到屏幕上。

细节

的AsynchTask

private class DownloadFacebookFriendsTask extends AsyncTask<FacebookFriend.Type, Boolean, Boolean> {

    private final String TAG = DownloadFacebookFriendsTask.class.getSimpleName();
    GraphObject graphObject;
    ArrayList<FacebookFriend> myList = new ArrayList<FacebookFriend>();

    @Override
    protected Boolean doInBackground(FacebookFriend.Type... pickType) {
        //
        // Determine Type
        //
        String facebookRequest;
        if (pickType[0] == FacebookFriend.Type.AVAILABLE) {
            facebookRequest = "/me/friends";
        } else {
            facebookRequest = "/me/invitable_friends";
        }

        //
        // Launch Facebook request and WAIT.
        //
        new Request(
            Session.getActiveSession(),
            facebookRequest,
            null,
            HttpMethod.GET,
            new Request.Callback() {
                public void onCompleted(Response response) {
                    FacebookRequestError error = response.getError();
                    if (error != null && response != null) {
                        Log.e(TAG, error.toString());
                    } else {
                        graphObject = response.getGraphObject();
                    }
                }
            }
        ).executeAndWait();

        //
        // Process Facebook response
        //
        //
        if (graphObject == null) {
            return false;
        }

        int numberOfRecords = 0;
        JSONArray dataArray = (JSONArray) graphObject.getProperty("data");
        if (dataArray.length() > 0) {

            // Ensure the user has at least one friend ...
            for (int i = 0; i < dataArray.length(); i++) {

                JSONObject jsonObject = dataArray.optJSONObject(i);
                FacebookFriend facebookFriend = new FacebookFriend(jsonObject, pickType[0]);

                if (facebookFriend.isValid()) {
                    numberOfRecords++;

                    myList.add(facebookFriend);
                }
            }
        }

        // Make sure there are records to process
        if (numberOfRecords > 0){
            return true;
        } else {
            return false;
        }
    }

    @Override
    protected void onProgressUpdate(Boolean... booleans) {
        // No need to update this, wait until the whole thread finishes.
    }

    @Override
    protected void onPostExecute(Boolean result) {
        if (result) {
            /*
            User the array "myList" to create the adapter which will control showing items in the list.
             */

        } else {
            Log.i(TAG, "Facebook Thread unable to Get/Parse friend data. Type = " + pickType);
        }
    }
}

我创建的FacebookFriend类

public class FacebookFriend {

    String facebookId;
    String name;
    String pictureUrl;
    boolean invitable;
    boolean available;
    boolean isValid;
    public enum Type {AVAILABLE, INVITABLE};

    public FacebookFriend(JSONObject jsonObject, Type type) {
        //
        //Parse the Facebook Data from the JSON object.
        //
        try {
            if (type == Type.INVITABLE) {
                //parse /me/invitable_friend
                this.facebookId =  jsonObject.getString("id");
                this.name = jsonObject.getString("name");

                // Handle the picture data.
                JSONObject pictureJsonObject = jsonObject.getJSONObject("picture").getJSONObject("data");
                boolean isSilhouette = pictureJsonObject.getBoolean("is_silhouette");
                if (!isSilhouette) {
                    this.pictureUrl = pictureJsonObject.getString("url");

                } else {
                    this.pictureUrl = "";
                }

                this.invitable = true;
            } else {
                // Parse /me/friends
                this.facebookId =  jsonObject.getString("id");
                this.name = jsonObject.getString("name");
                this.available = true;
                this.pictureUrl = "";
            }

            isValid = true;
        } catch (JSONException e) {
            Log.w("#", "Warnings - unable to process Facebook JSON: " + e.getLocalizedMessage());
        }
    }
}

其他回答

虽然Simon Cross的回答是正确的,但我还是想用一个例子(Android)来补充一下。我会尽量概括一下,只关注这个问题。就我个人而言,我把东西存储在数据库中,所以加载很顺利,但这需要一个CursorAdapter和ContentProvider,这有点超出了这里的范围。

我自己来到这里,然后想,现在该怎么办!

这个问题

就像user3594351一样,我注意到朋友数据是空白的。我发现这是通过使用FriendPickerFragment。三个月前有用的,现在没用了。就连Facebook的例子也失败了。所以我的问题是“我如何手工创建FriendPickerFragment ?”

什么不起作用

Simon Cross的选项1还不足以邀请好友加入应用。Simon Cross还推荐了请求对话框,但它一次只允许5个请求。请求对话框还会在任何特定的Facebook登录会话中显示相同的朋友。不是有用的。

什么有效(总结)

第二种选择,需要付出一些努力。你必须确保你遵守Facebook的新规则:1)你是第二场比赛。)你有一个Canvas应用(Web Presence)你的应用已经在Facebook上注册了。这一切都是在Facebook开发者网站的设置下完成的。

为了在我的应用程序中模拟好友选择器,我做了以下工作:

创建显示两个片段的选项卡活动。每个片段显示一个列表。一个片段用于可用的朋友(/me/friends),另一个片段用于可邀请的朋友(/me/invitable_friends)。使用相同的片段代码来呈现两个选项卡。 创建一个AsyncTask从Facebook获取好友数据。一旦数据被加载,将其扔给适配器,适配器将把值呈现到屏幕上。

细节

的AsynchTask

private class DownloadFacebookFriendsTask extends AsyncTask<FacebookFriend.Type, Boolean, Boolean> {

    private final String TAG = DownloadFacebookFriendsTask.class.getSimpleName();
    GraphObject graphObject;
    ArrayList<FacebookFriend> myList = new ArrayList<FacebookFriend>();

    @Override
    protected Boolean doInBackground(FacebookFriend.Type... pickType) {
        //
        // Determine Type
        //
        String facebookRequest;
        if (pickType[0] == FacebookFriend.Type.AVAILABLE) {
            facebookRequest = "/me/friends";
        } else {
            facebookRequest = "/me/invitable_friends";
        }

        //
        // Launch Facebook request and WAIT.
        //
        new Request(
            Session.getActiveSession(),
            facebookRequest,
            null,
            HttpMethod.GET,
            new Request.Callback() {
                public void onCompleted(Response response) {
                    FacebookRequestError error = response.getError();
                    if (error != null && response != null) {
                        Log.e(TAG, error.toString());
                    } else {
                        graphObject = response.getGraphObject();
                    }
                }
            }
        ).executeAndWait();

        //
        // Process Facebook response
        //
        //
        if (graphObject == null) {
            return false;
        }

        int numberOfRecords = 0;
        JSONArray dataArray = (JSONArray) graphObject.getProperty("data");
        if (dataArray.length() > 0) {

            // Ensure the user has at least one friend ...
            for (int i = 0; i < dataArray.length(); i++) {

                JSONObject jsonObject = dataArray.optJSONObject(i);
                FacebookFriend facebookFriend = new FacebookFriend(jsonObject, pickType[0]);

                if (facebookFriend.isValid()) {
                    numberOfRecords++;

                    myList.add(facebookFriend);
                }
            }
        }

        // Make sure there are records to process
        if (numberOfRecords > 0){
            return true;
        } else {
            return false;
        }
    }

    @Override
    protected void onProgressUpdate(Boolean... booleans) {
        // No need to update this, wait until the whole thread finishes.
    }

    @Override
    protected void onPostExecute(Boolean result) {
        if (result) {
            /*
            User the array "myList" to create the adapter which will control showing items in the list.
             */

        } else {
            Log.i(TAG, "Facebook Thread unable to Get/Parse friend data. Type = " + pickType);
        }
    }
}

我创建的FacebookFriend类

public class FacebookFriend {

    String facebookId;
    String name;
    String pictureUrl;
    boolean invitable;
    boolean available;
    boolean isValid;
    public enum Type {AVAILABLE, INVITABLE};

    public FacebookFriend(JSONObject jsonObject, Type type) {
        //
        //Parse the Facebook Data from the JSON object.
        //
        try {
            if (type == Type.INVITABLE) {
                //parse /me/invitable_friend
                this.facebookId =  jsonObject.getString("id");
                this.name = jsonObject.getString("name");

                // Handle the picture data.
                JSONObject pictureJsonObject = jsonObject.getJSONObject("picture").getJSONObject("data");
                boolean isSilhouette = pictureJsonObject.getBoolean("is_silhouette");
                if (!isSilhouette) {
                    this.pictureUrl = pictureJsonObject.getString("url");

                } else {
                    this.pictureUrl = "";
                }

                this.invitable = true;
            } else {
                // Parse /me/friends
                this.facebookId =  jsonObject.getString("id");
                this.name = jsonObject.getString("name");
                this.available = true;
                this.pictureUrl = "";
            }

            isValid = true;
        } catch (JSONException e) {
            Log.w("#", "Warnings - unable to process Facebook JSON: " + e.getLocalizedMessage());
        }
    }
}

正如Simon提到的,这在新的Facebook API中是不可能的。从技术上讲,你可以通过浏览器自动化来实现。

this is against Facebook policy, so depending on the country where you live, this may not be legal you'll have to use your credentials / ask user for credentials and possibly store them (storing passwords even symmetrically encrypted is not a good idea) when Facebook changes their API, you'll have to update the browser automation code as well (if you can't force updates of your application, you should put browser automation piece out as a webservice) this is bypassing the OAuth concept on the other hand, my feeling is that I'm owning my data including the list of my friends and Facebook shouldn't restrict me from accessing those via the API

使用WatiN的示例实现:

class FacebookUser
{
  public string Name { get; set; }
  public long Id { get; set; }
}

public IList<FacebookUser> GetFacebookFriends(string email, string password, int? maxTimeoutInMilliseconds)
{
  var users = new List<FacebookUser>();
  Settings.Instance.MakeNewIeInstanceVisible = false;
  using (var browser = new IE("https://www.facebook.com"))
  {
    try
    {
      browser.TextField(Find.ByName("email")).Value = email;
      browser.TextField(Find.ByName("pass")).Value = password;
      browser.Form(Find.ById("login_form")).Submit();
      browser.WaitForComplete();
    }
    catch (ElementNotFoundException)
    {
      // We're already logged in
    }
    browser.GoTo("https://www.facebook.com/friends");
    var watch = new Stopwatch();
    watch.Start();

    Link previousLastLink = null;
    while (maxTimeoutInMilliseconds.HasValue && watch.Elapsed.TotalMilliseconds < maxTimeoutInMilliseconds.Value)
    {
      var lastLink = browser.Links.Where(l => l.GetAttributeValue("data-hovercard") != null
                       && l.GetAttributeValue("data-hovercard").Contains("user.php")
                       && l.Text != null
                     ).LastOrDefault();

      if (lastLink == null || previousLastLink == lastLink)
      {
        break;
      }

      var ieElement = lastLink.NativeElement as IEElement;
      if (ieElement != null)
      {
        var htmlElement = ieElement.AsHtmlElement;
        htmlElement.scrollIntoView();
        browser.WaitForComplete();
      }

      previousLastLink = lastLink;
    }

    var links = browser.Links.Where(l => l.GetAttributeValue("data-hovercard") != null
      && l.GetAttributeValue("data-hovercard").Contains("user.php")
      && l.Text != null
    ).ToList();

    var idRegex = new Regex("id=(?<id>([0-9]+))");
    foreach (var link in links)
    {
      string hovercard = link.GetAttributeValue("data-hovercard");
      var match = idRegex.Match(hovercard);
      long id = 0;
      if (match.Success)
      {
        id = long.Parse(match.Groups["id"].Value);
      }
      users.Add(new FacebookUser
      {
        Name = link.Text,
        Id = id
      });
    }
  }
  return users;
}

该方法的原型实现(使用c# /WatiN)参见https://github.com/svejdo1/ShadowApi。它还允许动态更新Facebook连接器,检索您的联系人列表。

试着/我/ taggable_friends吗?limit=5000使用JavaScript代码

Or

试试Graph API:

https://graph.facebook.com/v2.3/user_id_here/taggable_friends?access_token=

如果你在开发模式上还在为这个问题而挣扎。 遵循如下所述的相同流程:

为你的主应用创建一个测试应用, 创建测试用户,自动为测试用户安装app,并为他们分配“user_friend”权限。 将测试用户添加为彼此的好友。

经过大量的研究,我遵循了同样的过程,最终它成功了。

Facebook现在已经修改了他们的政策。如果你的应用没有Canvas实现,如果你的应用不是游戏,你就无法获得整个好友列表。当然,还有taggable_friends,但那个只用于标记。

你将能够拉的朋友谁已授权的应用程序的名单。

使用Graph API 1.0的应用程序将持续到2015年4月30日,之后将被弃用。

请参阅以下内容了解更多详细信息:

用户的朋友 Facebook应用开发常见问题